@sun-asterisk/sunlint 1.0.5

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 (192) hide show
  1. package/CHANGELOG.md +202 -0
  2. package/LICENSE +21 -0
  3. package/README.md +490 -0
  4. package/cli-legacy.js +355 -0
  5. package/cli.js +35 -0
  6. package/config/default.json +22 -0
  7. package/config/presets/beginner.json +36 -0
  8. package/config/presets/ci.json +46 -0
  9. package/config/presets/recommended.json +24 -0
  10. package/config/presets/strict.json +32 -0
  11. package/config/rules-registry.json +681 -0
  12. package/config/sunlint-schema.json +166 -0
  13. package/config/typescript/custom-rules-new.js +0 -0
  14. package/config/typescript/custom-rules.js +9 -0
  15. package/config/typescript/eslint.config.js +110 -0
  16. package/config/typescript/package-lock.json +1585 -0
  17. package/config/typescript/package.json +13 -0
  18. package/config/typescript/security-rules/index.js +90 -0
  19. package/config/typescript/security-rules/s005-no-origin-auth.js +95 -0
  20. package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +69 -0
  21. package/config/typescript/security-rules/s008-crypto-agility.js +62 -0
  22. package/config/typescript/security-rules/s009-no-insecure-crypto.js +103 -0
  23. package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +123 -0
  24. package/config/typescript/security-rules/s011-no-insecure-uuid.js +66 -0
  25. package/config/typescript/security-rules/s012-hardcode-secret.js +71 -0
  26. package/config/typescript/security-rules/s014-insecure-tls-version.js +50 -0
  27. package/config/typescript/security-rules/s015-insecure-tls-certificate.js +43 -0
  28. package/config/typescript/security-rules/s016-sensitive-query-parameter.js +59 -0
  29. package/config/typescript/security-rules/s017-no-sql-injection.js +193 -0
  30. package/config/typescript/security-rules/s018-positive-input-validation.js +56 -0
  31. package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +113 -0
  32. package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +89 -0
  33. package/config/typescript/security-rules/s022-output-encoding.js +78 -0
  34. package/config/typescript/security-rules/s023-no-json-injection.js +300 -0
  35. package/config/typescript/security-rules/s025-server-side-input-validation.js +217 -0
  36. package/config/typescript/security-rules/s026-json-schema-validation.js +68 -0
  37. package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +80 -0
  38. package/config/typescript/security-rules/s029-require-csrf-protection.js +79 -0
  39. package/config/typescript/security-rules/s030-no-directory-browsing.js +78 -0
  40. package/config/typescript/security-rules/s033-require-samesite-cookie.js +80 -0
  41. package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +77 -0
  42. package/config/typescript/security-rules/s035-cookie-specific-path.js +74 -0
  43. package/config/typescript/security-rules/s036-no-unsafe-file-include.js +68 -0
  44. package/config/typescript/security-rules/s037-require-anti-cache-headers.js +70 -0
  45. package/config/typescript/security-rules/s038-no-version-disclosure.js +74 -0
  46. package/config/typescript/security-rules/s039-no-session-token-in-url.js +63 -0
  47. package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +211 -0
  48. package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +294 -0
  49. package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +254 -0
  50. package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +292 -0
  51. package/config/typescript/security-rules/s045-anti-automation-controls.js +46 -0
  52. package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +44 -0
  53. package/config/typescript/security-rules/s048-password-credential-recovery.js +54 -0
  54. package/config/typescript/security-rules/s050-session-token-weak-hash.js +94 -0
  55. package/config/typescript/security-rules/s052-secure-random-authentication-code.js +66 -0
  56. package/config/typescript/security-rules/s054-verification-default-account.js +109 -0
  57. package/config/typescript/security-rules/s057-utc-logging.js +54 -0
  58. package/config/typescript/security-rules/s058-no-ssrf.js +73 -0
  59. package/config/typescript/test-s005-working.ts +22 -0
  60. package/config/typescript/tsconfig.json +29 -0
  61. package/core/ai-analyzer.js +169 -0
  62. package/core/analysis-orchestrator.js +705 -0
  63. package/core/cli-action-handler.js +230 -0
  64. package/core/cli-program.js +106 -0
  65. package/core/config-manager.js +396 -0
  66. package/core/config-merger.js +136 -0
  67. package/core/config-override-processor.js +74 -0
  68. package/core/config-preset-resolver.js +65 -0
  69. package/core/config-source-loader.js +152 -0
  70. package/core/config-validator.js +126 -0
  71. package/core/dependency-manager.js +105 -0
  72. package/core/eslint-engine-service.js +312 -0
  73. package/core/eslint-instance-manager.js +104 -0
  74. package/core/eslint-integration-service.js +363 -0
  75. package/core/git-utils.js +170 -0
  76. package/core/multi-rule-runner.js +239 -0
  77. package/core/output-service.js +250 -0
  78. package/core/report-generator.js +320 -0
  79. package/core/rule-mapping-service.js +309 -0
  80. package/core/rule-selection-service.js +121 -0
  81. package/core/sunlint-engine-service.js +23 -0
  82. package/core/typescript-analyzer.js +262 -0
  83. package/core/typescript-engine.js +313 -0
  84. package/docs/AI.md +163 -0
  85. package/docs/ARCHITECTURE.md +78 -0
  86. package/docs/CI-CD-GUIDE.md +315 -0
  87. package/docs/COMMAND-EXAMPLES.md +256 -0
  88. package/docs/DEBUG.md +86 -0
  89. package/docs/DISTRIBUTION.md +153 -0
  90. package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
  91. package/docs/ESLINT_INTEGRATION.md +238 -0
  92. package/docs/FOLDER_STRUCTURE.md +59 -0
  93. package/docs/HEURISTIC_VS_AI.md +113 -0
  94. package/docs/README.md +32 -0
  95. package/docs/RELEASE_GUIDE.md +230 -0
  96. package/docs/RULE-RESPONSIBILITY-MATRIX.md +204 -0
  97. package/eslint-integration/.eslintrc.js +98 -0
  98. package/eslint-integration/cli.js +35 -0
  99. package/eslint-integration/eslint-plugin-custom/c002-no-duplicate-code.js +204 -0
  100. package/eslint-integration/eslint-plugin-custom/c003-no-vague-abbreviations.js +246 -0
  101. package/eslint-integration/eslint-plugin-custom/c006-function-name-verb-noun.js +207 -0
  102. package/eslint-integration/eslint-plugin-custom/c010-limit-block-nesting.js +90 -0
  103. package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +43 -0
  104. package/eslint-integration/eslint-plugin-custom/c014-abstract-dependency-preferred.js +38 -0
  105. package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +39 -0
  106. package/eslint-integration/eslint-plugin-custom/c018-no-generic-throw.js +335 -0
  107. package/eslint-integration/eslint-plugin-custom/c023-no-duplicate-variable-name-in-scope.js +142 -0
  108. package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +50 -0
  109. package/eslint-integration/eslint-plugin-custom/c029-catch-block-logging.js +80 -0
  110. package/eslint-integration/eslint-plugin-custom/c030-use-custom-error-classes.js +294 -0
  111. package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +34 -0
  112. package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +32 -0
  113. package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +64 -0
  114. package/eslint-integration/eslint-plugin-custom/c042-boolean-name-prefix.js +406 -0
  115. package/eslint-integration/eslint-plugin-custom/c043-no-console-or-print.js +300 -0
  116. package/eslint-integration/eslint-plugin-custom/c047-no-duplicate-retry-logic.js +239 -0
  117. package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +31 -0
  118. package/eslint-integration/eslint-plugin-custom/c076-one-assert-per-test.js +184 -0
  119. package/eslint-integration/eslint-plugin-custom/index.js +155 -0
  120. package/eslint-integration/eslint-plugin-custom/package.json +13 -0
  121. package/eslint-integration/eslint-plugin-custom/package.json.bak +9 -0
  122. package/eslint-integration/eslint-plugin-custom/s003-no-unvalidated-redirect.js +86 -0
  123. package/eslint-integration/eslint-plugin-custom/s005-no-origin-auth.js +95 -0
  124. package/eslint-integration/eslint-plugin-custom/s006-activation-recovery-secret-not-plaintext.js +69 -0
  125. package/eslint-integration/eslint-plugin-custom/s008-crypto-agility.js +62 -0
  126. package/eslint-integration/eslint-plugin-custom/s009-no-insecure-crypto.js +103 -0
  127. package/eslint-integration/eslint-plugin-custom/s010-no-insecure-random-in-sensitive-context.js +123 -0
  128. package/eslint-integration/eslint-plugin-custom/s011-no-insecure-uuid.js +66 -0
  129. package/eslint-integration/eslint-plugin-custom/s012-hardcode-secret.js +71 -0
  130. package/eslint-integration/eslint-plugin-custom/s014-insecure-tls-version.js +50 -0
  131. package/eslint-integration/eslint-plugin-custom/s015-insecure-tls-certificate.js +43 -0
  132. package/eslint-integration/eslint-plugin-custom/s016-sensitive-query-parameter.js +59 -0
  133. package/eslint-integration/eslint-plugin-custom/s017-no-sql-injection.js +193 -0
  134. package/eslint-integration/eslint-plugin-custom/s018-positive-input-validation.js +56 -0
  135. package/eslint-integration/eslint-plugin-custom/s019-no-raw-user-input-in-email.js +113 -0
  136. package/eslint-integration/eslint-plugin-custom/s020-no-eval-dynamic-execution.js +89 -0
  137. package/eslint-integration/eslint-plugin-custom/s022-output-encoding.js +78 -0
  138. package/eslint-integration/eslint-plugin-custom/s023-no-json-injection.js +300 -0
  139. package/eslint-integration/eslint-plugin-custom/s025-server-side-input-validation.js +217 -0
  140. package/eslint-integration/eslint-plugin-custom/s026-json-schema-validation.js +68 -0
  141. package/eslint-integration/eslint-plugin-custom/s027-no-hardcoded-secrets.js +80 -0
  142. package/eslint-integration/eslint-plugin-custom/s029-require-csrf-protection.js +79 -0
  143. package/eslint-integration/eslint-plugin-custom/s030-no-directory-browsing.js +78 -0
  144. package/eslint-integration/eslint-plugin-custom/s033-require-samesite-cookie.js +80 -0
  145. package/eslint-integration/eslint-plugin-custom/s034-require-host-cookie-prefix.js +77 -0
  146. package/eslint-integration/eslint-plugin-custom/s035-cookie-specific-path.js +74 -0
  147. package/eslint-integration/eslint-plugin-custom/s036-no-unsafe-file-include.js +68 -0
  148. package/eslint-integration/eslint-plugin-custom/s037-require-anti-cache-headers.js +70 -0
  149. package/eslint-integration/eslint-plugin-custom/s038-no-version-disclosure.js +74 -0
  150. package/eslint-integration/eslint-plugin-custom/s039-no-session-token-in-url.js +63 -0
  151. package/eslint-integration/eslint-plugin-custom/s041-require-session-invalidate-on-logout.js +211 -0
  152. package/eslint-integration/eslint-plugin-custom/s042-require-periodic-reauthentication.js +294 -0
  153. package/eslint-integration/eslint-plugin-custom/s043-terminate-sessions-on-password-change.js +254 -0
  154. package/eslint-integration/eslint-plugin-custom/s044-require-full-session-for-sensitive-operations.js +292 -0
  155. package/eslint-integration/eslint-plugin-custom/s045-anti-automation-controls.js +46 -0
  156. package/eslint-integration/eslint-plugin-custom/s046-secure-notification-on-auth-change.js +44 -0
  157. package/eslint-integration/eslint-plugin-custom/s047-secure-random-passwords.js +108 -0
  158. package/eslint-integration/eslint-plugin-custom/s048-password-credential-recovery.js +54 -0
  159. package/eslint-integration/eslint-plugin-custom/s050-session-token-weak-hash.js +94 -0
  160. package/eslint-integration/eslint-plugin-custom/s052-secure-random-authentication-code.js +66 -0
  161. package/eslint-integration/eslint-plugin-custom/s054-verification-default-account.js +109 -0
  162. package/eslint-integration/eslint-plugin-custom/s055-verification-rest-check-the-incoming-content-type.js +143 -0
  163. package/eslint-integration/eslint-plugin-custom/s057-utc-logging.js +54 -0
  164. package/eslint-integration/eslint-plugin-custom/s058-no-ssrf.js +73 -0
  165. package/eslint-integration/eslint-plugin-custom/t002-interface-prefix-i.js +42 -0
  166. package/eslint-integration/eslint-plugin-custom/t003-ts-ignore-reason.js +48 -0
  167. package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +160 -0
  168. package/eslint-integration/eslint-plugin-custom/t007-no-fn-in-constructor.js +52 -0
  169. package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +175 -0
  170. package/eslint-integration/eslint-plugin-custom/t019-no-empty-type.js +95 -0
  171. package/eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js +48 -0
  172. package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +377 -0
  173. package/eslint-integration/eslint.config.js +125 -0
  174. package/eslint-integration/eslint.config.simple.js +24 -0
  175. package/eslint-integration/node_modules/eslint-plugin-custom/package.json +0 -0
  176. package/eslint-integration/package.json +23 -0
  177. package/eslint-integration/sample.ts +53 -0
  178. package/eslint-integration/test-s003.js +5 -0
  179. package/eslint-integration/tsconfig.json +27 -0
  180. package/examples/.github/workflows/code-quality.yml +111 -0
  181. package/examples/.sunlint.json +42 -0
  182. package/examples/README.md +47 -0
  183. package/examples/package.json +33 -0
  184. package/package.json +100 -0
  185. package/rules/C006_function_naming/analyzer.js +338 -0
  186. package/rules/C006_function_naming/config.json +86 -0
  187. package/rules/C019_log_level_usage/analyzer.js +359 -0
  188. package/rules/C019_log_level_usage/config.json +121 -0
  189. package/rules/C029_catch_block_logging/analyzer.js +339 -0
  190. package/rules/C029_catch_block_logging/config.json +59 -0
  191. package/rules/C031_validation_separation/README.md +72 -0
  192. package/rules/C031_validation_separation/analyzer.js +186 -0
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Custom ESLint rule for: T026 – Limit deeply nested generics
3
+ * Rule ID: custom/t026
4
+ * Purpose: Prevent deeply nested generic type parameters to maintain type readability and reduce cognitive load
5
+ *
6
+ * Following Vietnamese Coding Standards:
7
+ * - Rule C005: Each function should do one thing (this rule focuses solely on generic nesting)
8
+ * - Rule C015: Use domain language in naming (clear error messages in both English and Vietnamese)
9
+ */
10
+
11
+ "use strict";
12
+
13
+ module.exports = {
14
+ meta: {
15
+ type: "suggestion",
16
+ docs: {
17
+ description: "Limit deeply nested generics to maintain type readability",
18
+ category: "TypeScript",
19
+ recommended: true,
20
+ },
21
+ fixable: null,
22
+ schema: [
23
+ {
24
+ type: "object",
25
+ properties: {
26
+ maxDepth: {
27
+ type: "integer",
28
+ minimum: 1,
29
+ description: "Maximum allowed depth of nested generics (default: 3)"
30
+ },
31
+ allowUtilityTypes: {
32
+ type: "boolean",
33
+ description: "Whether to allow deeper nesting in utility types like Partial, Pick, etc. (default: true)"
34
+ },
35
+ exemptedTypeNames: {
36
+ type: "array",
37
+ items: { type: "string" },
38
+ description: "List of type names that are exempt from the depth check"
39
+ }
40
+ },
41
+ additionalProperties: false
42
+ }
43
+ ],
44
+ messages: {
45
+ tooDeepNesting: "Generic type nesting is too deep ({{depth}} levels). Maximum allowed is {{maxDepth}} levels. Consider breaking down complex types into simpler ones.",
46
+ tooDeepNestingVietnamese: "Kiểu generic lồng quá sâu ({{depth}} cấp). Tối đa cho phép là {{maxDepth}} cấp. Hãy chia nhỏ kiểu phức tạp thành các kiểu đơn giản hơn."
47
+ },
48
+ },
49
+
50
+ create(context) {
51
+ const options = context.options[0] || {};
52
+ const maxDepth = options.maxDepth || 3;
53
+ const allowUtilityTypes = options.allowUtilityTypes !== false;
54
+ const exemptedTypeNames = new Set(options.exemptedTypeNames || []);
55
+
56
+ // Common TypeScript utility types that might need deeper nesting
57
+ const utilityTypes = new Set([
58
+ 'Partial', 'Required', 'Readonly', 'Pick', 'Omit', 'Exclude', 'Extract',
59
+ 'NonNullable', 'Parameters', 'ConstructorParameters', 'ReturnType',
60
+ 'InstanceType', 'ThisParameterType', 'OmitThisParameter', 'ThisType',
61
+ 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize'
62
+ // Note: Removed Promise, Array, Record, Map, Set to properly catch deep nesting
63
+ ]);
64
+
65
+ /**
66
+ * Calculate the depth of nested generics in a type node
67
+ * @param {Object} node - The TypeScript AST node
68
+ * @param {number} currentDepth - Current depth level
69
+ * @returns {number} The maximum depth found
70
+ */
71
+ function calculateGenericDepth(node, currentDepth = 0) {
72
+ if (!node) return currentDepth;
73
+
74
+ let maxDepth = currentDepth;
75
+
76
+ switch (node.type) {
77
+ case 'TSTypeReference':
78
+ // Check typeArguments (not typeParameters) for type references
79
+ if (node.typeArguments && node.typeArguments.params) {
80
+ const newDepth = currentDepth + 1;
81
+ for (const param of node.typeArguments.params) {
82
+ const paramDepth = calculateGenericDepth(param, newDepth);
83
+ maxDepth = Math.max(maxDepth, paramDepth);
84
+ }
85
+ }
86
+ break;
87
+
88
+ case 'TSUnionType':
89
+ case 'TSIntersectionType':
90
+ if (node.types) {
91
+ for (const type of node.types) {
92
+ const typeDepth = calculateGenericDepth(type, currentDepth);
93
+ maxDepth = Math.max(maxDepth, typeDepth);
94
+ }
95
+ }
96
+ break;
97
+
98
+ case 'TSTupleType':
99
+ if (node.elementTypes) {
100
+ for (const element of node.elementTypes) {
101
+ const elementDepth = calculateGenericDepth(element, currentDepth);
102
+ maxDepth = Math.max(maxDepth, elementDepth);
103
+ }
104
+ }
105
+ break;
106
+
107
+ case 'TSArrayType':
108
+ if (node.elementType) {
109
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.elementType, currentDepth));
110
+ }
111
+ break;
112
+
113
+ case 'TSMappedType':
114
+ if (node.typeParameter) {
115
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.typeParameter, currentDepth));
116
+ }
117
+ if (node.typeAnnotation) {
118
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.typeAnnotation, currentDepth));
119
+ }
120
+ break;
121
+
122
+ case 'TSConditionalType':
123
+ if (node.checkType) {
124
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.checkType, currentDepth));
125
+ }
126
+ if (node.extendsType) {
127
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.extendsType, currentDepth));
128
+ }
129
+ if (node.trueType) {
130
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.trueType, currentDepth));
131
+ }
132
+ if (node.falseType) {
133
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.falseType, currentDepth));
134
+ }
135
+ break;
136
+
137
+ case 'TSIndexedAccessType':
138
+ if (node.objectType) {
139
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.objectType, currentDepth));
140
+ }
141
+ if (node.indexType) {
142
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.indexType, currentDepth));
143
+ }
144
+ break;
145
+
146
+ case 'TSTypeQuery':
147
+ if (node.exprName) {
148
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.exprName, currentDepth));
149
+ }
150
+ break;
151
+
152
+ case 'TSFunctionType':
153
+ case 'TSConstructorType':
154
+ if (node.typeParameters && node.typeParameters.params) {
155
+ for (const param of node.typeParameters.params) {
156
+ const paramDepth = calculateGenericDepth(param, currentDepth + 1);
157
+ maxDepth = Math.max(maxDepth, paramDepth);
158
+ }
159
+ }
160
+ if (node.parameters) {
161
+ for (const param of node.parameters) {
162
+ if (param.typeAnnotation) {
163
+ const paramDepth = calculateGenericDepth(param.typeAnnotation.typeAnnotation, currentDepth);
164
+ maxDepth = Math.max(maxDepth, paramDepth);
165
+ }
166
+ }
167
+ }
168
+ if (node.typeAnnotation) {
169
+ maxDepth = Math.max(maxDepth, calculateGenericDepth(node.typeAnnotation.typeAnnotation, currentDepth));
170
+ }
171
+ break;
172
+ }
173
+
174
+ return maxDepth;
175
+ }
176
+
177
+ /**
178
+ * Check if a type reference is a utility type that should be exempted
179
+ * @param {Object} node - The TypeScript type reference node
180
+ * @returns {boolean} Whether this is an exempted utility type
181
+ */
182
+ function isExemptedType(node) {
183
+ if (node.type !== 'TSTypeReference' || !node.typeName) {
184
+ return false;
185
+ }
186
+
187
+ let typeName = '';
188
+ if (node.typeName.type === 'Identifier') {
189
+ typeName = node.typeName.name;
190
+ } else if (node.typeName.type === 'TSQualifiedName') {
191
+ // Handle qualified names like React.Component
192
+ typeName = getQualifiedTypeName(node.typeName);
193
+ }
194
+
195
+ return exemptedTypeNames.has(typeName) ||
196
+ (allowUtilityTypes && utilityTypes.has(typeName));
197
+ }
198
+
199
+ /**
200
+ * Get the full qualified type name from a TSQualifiedName node
201
+ * @param {Object} node - The TSQualifiedName node
202
+ * @returns {string} The full qualified name
203
+ */
204
+ function getQualifiedTypeName(node) {
205
+ if (node.type === 'Identifier') {
206
+ return node.name;
207
+ }
208
+ if (node.type === 'TSQualifiedName') {
209
+ return `${getQualifiedTypeName(node.left)}.${node.right.name}`;
210
+ }
211
+ return '';
212
+ }
213
+
214
+ /**
215
+ * Check a type node for excessive generic nesting
216
+ * @param {Object} node - The TypeScript AST node to check
217
+ */
218
+ function checkTypeNode(node) {
219
+ if (!node) return;
220
+
221
+ // Skip utility types if allowed
222
+ if (isExemptedType(node)) {
223
+ return;
224
+ }
225
+
226
+ const depth = calculateGenericDepth(node);
227
+
228
+ if (depth > maxDepth) {
229
+ context.report({
230
+ node,
231
+ messageId: "tooDeepNesting",
232
+ data: {
233
+ depth,
234
+ maxDepth
235
+ }
236
+ });
237
+ }
238
+ }
239
+
240
+ return {
241
+ // Check type aliases
242
+ TSTypeAliasDeclaration(node) {
243
+ if (node.typeAnnotation) {
244
+ checkTypeNode(node.typeAnnotation);
245
+ }
246
+ },
247
+
248
+ // Check interface declarations
249
+ TSInterfaceDeclaration(node) {
250
+ if (node.extends) {
251
+ for (const extend of node.extends) {
252
+ checkTypeNode(extend);
253
+ }
254
+ }
255
+
256
+ if (node.body && node.body.body) {
257
+ for (const member of node.body.body) {
258
+ if (member.typeAnnotation && member.typeAnnotation.typeAnnotation) {
259
+ checkTypeNode(member.typeAnnotation.typeAnnotation);
260
+ }
261
+ }
262
+ }
263
+ },
264
+
265
+ // Check function declarations
266
+ FunctionDeclaration(node) {
267
+ if (node.typeParameters && node.typeParameters.params) {
268
+ for (const param of node.typeParameters.params) {
269
+ if (param.constraint) {
270
+ checkTypeNode(param.constraint);
271
+ }
272
+ if (param.default) {
273
+ checkTypeNode(param.default);
274
+ }
275
+ }
276
+ }
277
+ },
278
+
279
+ // Check function expressions and arrow functions
280
+ FunctionExpression(node) {
281
+ if (node.typeParameters && node.typeParameters.params) {
282
+ for (const param of node.typeParameters.params) {
283
+ if (param.constraint) {
284
+ checkTypeNode(param.constraint);
285
+ }
286
+ if (param.default) {
287
+ checkTypeNode(param.default);
288
+ }
289
+ }
290
+ }
291
+ },
292
+
293
+ ArrowFunctionExpression(node) {
294
+ if (node.typeParameters && node.typeParameters.params) {
295
+ for (const param of node.typeParameters.params) {
296
+ if (param.constraint) {
297
+ checkTypeNode(param.constraint);
298
+ }
299
+ if (param.default) {
300
+ checkTypeNode(param.default);
301
+ }
302
+ }
303
+ }
304
+ },
305
+
306
+ // Check method definitions
307
+ MethodDefinition(node) {
308
+ if (node.value && node.value.typeParameters && node.value.typeParameters.params) {
309
+ for (const param of node.value.typeParameters.params) {
310
+ if (param.constraint) {
311
+ checkTypeNode(param.constraint);
312
+ }
313
+ if (param.default) {
314
+ checkTypeNode(param.default);
315
+ }
316
+ }
317
+ }
318
+ },
319
+
320
+ // Check variable declarations with type annotations
321
+ VariableDeclarator(node) {
322
+ if (node.id && node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
323
+ checkTypeNode(node.id.typeAnnotation.typeAnnotation);
324
+ }
325
+ },
326
+
327
+ // Check property signatures
328
+ TSPropertySignature(node) {
329
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
330
+ checkTypeNode(node.typeAnnotation.typeAnnotation);
331
+ }
332
+ },
333
+
334
+ // Check method signatures
335
+ TSMethodSignature(node) {
336
+ if (node.typeParameters && node.typeParameters.params) {
337
+ for (const param of node.typeParameters.params) {
338
+ if (param.constraint) {
339
+ checkTypeNode(param.constraint);
340
+ }
341
+ if (param.default) {
342
+ checkTypeNode(param.default);
343
+ }
344
+ }
345
+ }
346
+ },
347
+
348
+ // Check call signatures
349
+ TSCallSignatureDeclaration(node) {
350
+ if (node.typeParameters && node.typeParameters.params) {
351
+ for (const param of node.typeParameters.params) {
352
+ if (param.constraint) {
353
+ checkTypeNode(param.constraint);
354
+ }
355
+ if (param.default) {
356
+ checkTypeNode(param.default);
357
+ }
358
+ }
359
+ }
360
+ },
361
+
362
+ // Check construct signatures
363
+ TSConstructSignatureDeclaration(node) {
364
+ if (node.typeParameters && node.typeParameters.params) {
365
+ for (const param of node.typeParameters.params) {
366
+ if (param.constraint) {
367
+ checkTypeNode(param.constraint);
368
+ }
369
+ if (param.default) {
370
+ checkTypeNode(param.default);
371
+ }
372
+ }
373
+ }
374
+ }
375
+ };
376
+ },
377
+ };
@@ -0,0 +1,125 @@
1
+ // ESLint Flat Configuration for SunLint (ESLint v9+)
2
+ // Following Rule C005: Single responsibility - ESLint configuration
3
+ const typescriptParser = require('@typescript-eslint/parser');
4
+ const typescriptPlugin = require('@typescript-eslint/eslint-plugin');
5
+ const customPlugin = require('./eslint-plugin-custom');
6
+
7
+ module.exports = [
8
+ {
9
+ files: ['**/*.{js,ts,tsx,jsx}'],
10
+ languageOptions: {
11
+ parser: typescriptParser,
12
+ parserOptions: {
13
+ ecmaVersion: 2022,
14
+ sourceType: 'module',
15
+ project: './tsconfig.json'
16
+ },
17
+ globals: {
18
+ console: 'readonly',
19
+ process: 'readonly',
20
+ eval: 'readonly',
21
+ Buffer: 'readonly',
22
+ __dirname: 'readonly',
23
+ __filename: 'readonly',
24
+ exports: 'writable',
25
+ module: 'writable',
26
+ require: 'readonly',
27
+ global: 'readonly'
28
+ }
29
+ },
30
+ plugins: {
31
+ 'custom': customPlugin,
32
+ '@typescript-eslint': typescriptPlugin
33
+ },
34
+ rules: {
35
+ // Rule C019: No console.error for non-critical errors
36
+ 'no-console': ['warn', { allow: ['warn', 'log'] }],
37
+
38
+ // Code quality rules
39
+ 'no-unused-vars': 'warn',
40
+ 'prefer-const': 'warn',
41
+ 'no-var': 'error',
42
+ 'no-undef': 'warn',
43
+
44
+ // Rule C005: Single responsibility principle
45
+ 'max-lines-per-function': ['warn', { max: 50 }],
46
+ 'complexity': ['warn', { max: 10 }],
47
+
48
+ // Rule C014: Avoid direct new instantiation patterns
49
+ 'no-new': 'warn',
50
+
51
+ // Security and best practices
52
+ 'no-eval': 'error',
53
+ 'no-implied-eval': 'error',
54
+ 'no-new-func': 'error',
55
+ 'eqeqeq': 'error',
56
+ 'curly': 'error',
57
+
58
+ // Quality rules (dynamic loading based on category)
59
+ 'custom/c002': 'off',
60
+ 'custom/c003': 'off',
61
+ 'custom/c006': 'off',
62
+ 'custom/c010': 'off',
63
+ 'custom/c013': 'off',
64
+ 'custom/c014': 'off',
65
+ 'custom/c017': 'off',
66
+ 'custom/c018': 'off',
67
+ 'custom/c023': 'off',
68
+ 'custom/c027': 'off',
69
+ 'custom/c029': 'off',
70
+ 'custom/c030': 'off',
71
+ 'custom/c034': 'off',
72
+ 'custom/c035': 'off',
73
+ 'custom/c041': 'off',
74
+ 'custom/c042': 'off',
75
+ 'custom/c043': 'off',
76
+ 'custom/c047': 'off',
77
+ 'custom/c048': 'off',
78
+
79
+ // Security rules (dynamic loading based on category)
80
+ 'custom/typescript_s003': 'off',
81
+ 'custom/typescript_s005': 'off',
82
+ 'custom/typescript_s006': 'off',
83
+ 'custom/typescript_s008': 'off',
84
+ 'custom/typescript_s009': 'off',
85
+ 'custom/typescript_s010': 'off',
86
+ 'custom/typescript_s011': 'off',
87
+ 'custom/typescript_s012': 'off',
88
+ 'custom/typescript_s014': 'off',
89
+ 'custom/typescript_s015': 'off',
90
+ 'custom/typescript_s016': 'off',
91
+ 'custom/typescript_s017': 'off',
92
+ 'custom/typescript_s018': 'off',
93
+ 'custom/typescript_s019': 'off',
94
+ 'custom/typescript_s020': 'off',
95
+ 'custom/typescript_s022': 'off',
96
+ 'custom/typescript_s023': 'off',
97
+ 'custom/typescript_s025': 'off',
98
+ 'custom/typescript_s026': 'off',
99
+ 'custom/typescript_s027': 'off',
100
+ 'custom/typescript_s029': 'off',
101
+ 'custom/typescript_s030': 'off',
102
+ 'custom/typescript_s033': 'off',
103
+ 'custom/typescript_s034': 'off',
104
+ 'custom/typescript_s035': 'off',
105
+ 'custom/typescript_s036': 'off',
106
+ 'custom/typescript_s037': 'off',
107
+ 'custom/typescript_s038': 'off',
108
+ 'custom/typescript_s039': 'off',
109
+ 'custom/typescript_s041': 'off',
110
+ 'custom/typescript_s042': 'off',
111
+ 'custom/typescript_s043': 'off',
112
+ 'custom/typescript_s044': 'off',
113
+ 'custom/typescript_s045': 'off',
114
+ 'custom/typescript_s046': 'off',
115
+ 'custom/typescript_s047': 'off',
116
+ 'custom/typescript_s048': 'off',
117
+ 'custom/typescript_s050': 'off',
118
+ 'custom/typescript_s052': 'off',
119
+ 'custom/typescript_s054': 'off',
120
+ 'custom/typescript_s055': 'off',
121
+ 'custom/typescript_s057': 'off',
122
+ 'custom/typescript_s058': 'off'
123
+ }
124
+ }
125
+ ];
@@ -0,0 +1,24 @@
1
+ // Simple ESLint Flat Configuration for Testing
2
+ module.exports = [
3
+ {
4
+ files: ['**/*.{js,mjs,cjs}'],
5
+ languageOptions: {
6
+ ecmaVersion: 2022,
7
+ sourceType: 'module',
8
+ globals: {
9
+ console: 'readonly',
10
+ process: 'readonly',
11
+ eval: 'readonly'
12
+ }
13
+ },
14
+ rules: {
15
+ 'no-console': ['warn', { allow: ['warn', 'log'] }],
16
+ 'no-unused-vars': 'warn',
17
+ 'prefer-const': 'warn',
18
+ 'no-var': 'error',
19
+ 'no-undef': 'warn',
20
+ 'no-eval': 'error',
21
+ 'eqeqeq': 'error'
22
+ }
23
+ }
24
+ ];
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "typescript",
3
+ "private": true,
4
+ "devDependencies": {
5
+ "@types/bun": "latest",
6
+ "@typescript-eslint/eslint-plugin": "^8.34.1",
7
+ "@typescript-eslint/parser": "^8.34.1",
8
+ "@typescript-eslint/utils": "^8.34.1",
9
+ "eslint": "^9.29.0",
10
+ "@eslint/eslintrc": "^3.3.1",
11
+ "@eslint/js": "^9.28.0",
12
+ "eslint-plugin-sonarjs": "^3.0.2"
13
+ },
14
+ "peerDependencies": {
15
+ "typescript": "^5"
16
+ },
17
+ "scripts": {
18
+ "lint": "eslint .",
19
+ "lint:ts": "eslint src/ --ext .ts,.tsx"
20
+ },
21
+ "dependencies": {
22
+ }
23
+ }
@@ -0,0 +1,53 @@
1
+ // Test file to verify ESLint integration
2
+ class UserManager {
3
+ private users: any[] = [];
4
+
5
+ constructor() {
6
+ // Rule C032: API call in constructor (should be flagged)
7
+ this.fetchUsers();
8
+ }
9
+
10
+ // Rule C006: Not verb-noun naming (should be flagged)
11
+ data() {
12
+ return this.users;
13
+ }
14
+
15
+ // Rule C019: Using console.error for non-critical (should be flagged)
16
+ processUser(user: any) {
17
+ if (!user.name) {
18
+ console.error('User name is missing');
19
+ return;
20
+ }
21
+ this.users.push(user);
22
+ }
23
+
24
+ // Rule C014: Direct new instead of DI (should be flagged)
25
+ createValidator() {
26
+ return new UserValidator();
27
+ }
28
+
29
+ private fetchUsers() {
30
+ // Mock API call
31
+ return fetch('/api/users');
32
+ }
33
+ }
34
+
35
+ class UserValidator {
36
+ validate(user: any) {
37
+ return user.name && user.email;
38
+ }
39
+ }
40
+
41
+ // Rule C005: Function doing multiple things (should be flagged)
42
+ function processAndValidateUser(user: any) {
43
+ // Validation logic
44
+ if (!user.name) {
45
+ return false;
46
+ }
47
+
48
+ // Processing logic
49
+ user.processedAt = new Date();
50
+ return true;
51
+ }
52
+
53
+ export { UserManager, UserValidator, processAndValidateUser };
@@ -0,0 +1,5 @@
1
+ // Test S003 rule patterns
2
+ window.location.href = req.query.redirectUrl; // Should match AssignmentExpression
3
+ location.href = req.body.targetUrl; // Should match AssignmentExpression
4
+ location.replace = req.params.url; // Should match AssignmentExpression
5
+ res.redirect(req.query.returnUrl); // Should match CallExpression
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Enable latest features
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+
22
+ // Some stricter flags (disabled by default)
23
+ "noUnusedLocals": false,
24
+ "noUnusedParameters": false,
25
+ "noPropertyAccessFromIndexSignature": false
26
+ }
27
+ }