@sun-asterisk/sunlint 1.0.7 → 1.1.3

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 (214) hide show
  1. package/.sunlint.json +35 -0
  2. package/CHANGELOG.md +30 -3
  3. package/CONTRIBUTING.md +235 -0
  4. package/PROJECT_STRUCTURE.md +60 -0
  5. package/README.md +73 -52
  6. package/cli.js +1 -0
  7. package/config/README.md +88 -0
  8. package/config/defaults/ai-rules-context.json +231 -0
  9. package/config/engines/engines.json +49 -0
  10. package/config/engines/eslint-rule-mapping.json +74 -0
  11. package/config/eslint-rule-mapping.json +126 -0
  12. package/config/integrations/eslint/base.config.js +125 -0
  13. package/config/integrations/eslint/simple.config.js +24 -0
  14. package/config/presets/strict.json +0 -1
  15. package/config/rule-analysis-strategies.js +74 -0
  16. package/config/{rules-registry.json → rules/rules-registry.json} +22 -0
  17. package/core/analysis-orchestrator.js +383 -591
  18. package/core/ast-modules/README.md +103 -0
  19. package/core/ast-modules/base-parser.js +90 -0
  20. package/core/ast-modules/index.js +97 -0
  21. package/core/ast-modules/package.json +37 -0
  22. package/core/ast-modules/parsers/eslint-js-parser.js +147 -0
  23. package/core/ast-modules/parsers/eslint-ts-parser.js +106 -0
  24. package/core/ast-modules/parsers/javascript-parser.js +187 -0
  25. package/core/ast-modules/parsers/typescript-parser.js +187 -0
  26. package/core/cli-action-handler.js +271 -255
  27. package/core/cli-program.js +18 -4
  28. package/core/config-manager.js +9 -3
  29. package/core/config-merger.js +40 -1
  30. package/core/config-validator.js +2 -2
  31. package/core/enhanced-rules-registry.js +331 -0
  32. package/core/file-targeting-service.js +92 -23
  33. package/core/interfaces/analysis-engine.interface.js +100 -0
  34. package/core/multi-rule-runner.js +0 -221
  35. package/core/output-service.js +1 -1
  36. package/core/rule-mapping-service.js +1 -1
  37. package/core/rule-selection-service.js +10 -2
  38. package/docs/AI.md +163 -0
  39. package/docs/ARCHITECTURE.md +78 -0
  40. package/docs/CI-CD-GUIDE.md +315 -0
  41. package/docs/COMMAND-EXAMPLES.md +256 -0
  42. package/docs/CONFIGURATION.md +414 -0
  43. package/docs/DEBUG.md +86 -0
  44. package/docs/DEPLOYMENT-STRATEGIES.md +270 -0
  45. package/docs/DISTRIBUTION.md +153 -0
  46. package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
  47. package/docs/ESLINT_INTEGRATION.md +238 -0
  48. package/docs/FOLDER_STRUCTURE.md +59 -0
  49. package/docs/HEURISTIC_VS_AI.md +113 -0
  50. package/docs/README.md +32 -0
  51. package/docs/RELEASE_GUIDE.md +230 -0
  52. package/engines/eslint-engine.js +601 -0
  53. package/engines/heuristic-engine.js +860 -0
  54. package/engines/openai-engine.js +374 -0
  55. package/engines/tree-sitter-parser.js +0 -0
  56. package/engines/universal-ast-engine.js +0 -0
  57. package/integrations/eslint/README.md +99 -0
  58. package/integrations/eslint/configs/.eslintrc.js +98 -0
  59. package/integrations/eslint/configs/eslint.config.js +133 -0
  60. package/integrations/eslint/configs/eslint.config.simple.js +24 -0
  61. package/integrations/eslint/package.json +23 -0
  62. package/integrations/eslint/plugin/index.js +164 -0
  63. package/integrations/eslint/plugin/package.json +13 -0
  64. package/integrations/eslint/plugin/rules/common/c002-no-duplicate-code.js +204 -0
  65. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +246 -0
  66. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +216 -0
  67. package/integrations/eslint/plugin/rules/common/c010-limit-block-nesting.js +90 -0
  68. package/integrations/eslint/plugin/rules/common/c013-no-dead-code.js +78 -0
  69. package/integrations/eslint/plugin/rules/common/c014-abstract-dependency-preferred.js +38 -0
  70. package/integrations/eslint/plugin/rules/common/c017-limit-constructor-logic.js +146 -0
  71. package/integrations/eslint/plugin/rules/common/c018-no-generic-throw.js +335 -0
  72. package/integrations/eslint/plugin/rules/common/c023-no-duplicate-variable-name-in-scope.js +142 -0
  73. package/integrations/eslint/plugin/rules/common/c029-catch-block-logging.js +115 -0
  74. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +294 -0
  75. package/integrations/eslint/plugin/rules/common/c035-no-empty-catch.js +162 -0
  76. package/integrations/eslint/plugin/rules/common/c041-no-config-inline.js +122 -0
  77. package/integrations/eslint/plugin/rules/common/c042-boolean-name-prefix.js +406 -0
  78. package/integrations/eslint/plugin/rules/common/c043-no-console-or-print.js +300 -0
  79. package/integrations/eslint/plugin/rules/common/c047-no-duplicate-retry-logic.js +239 -0
  80. package/integrations/eslint/plugin/rules/common/c072-one-assert-per-test.js +184 -0
  81. package/integrations/eslint/plugin/rules/common/c075-explicit-function-return-types.js +168 -0
  82. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +254 -0
  83. package/integrations/eslint/plugin/rules/security/s001-fail-securely.js +381 -0
  84. package/integrations/eslint/plugin/rules/security/s002-idor-check.js +945 -0
  85. package/integrations/eslint/plugin/rules/security/s003-no-unvalidated-redirect.js +86 -0
  86. package/integrations/eslint/plugin/rules/security/s007-no-plaintext-otp.js +74 -0
  87. package/integrations/eslint/plugin/rules/security/s013-verify-tls-connection.js +47 -0
  88. package/integrations/eslint/plugin/rules/security/s047-secure-random-passwords.js +108 -0
  89. package/integrations/eslint/plugin/rules/security/s055-verification-rest-check-the-incoming-content-type.js +143 -0
  90. package/integrations/eslint/plugin/rules/typescript/t002-interface-prefix-i.js +42 -0
  91. package/integrations/eslint/plugin/rules/typescript/t003-ts-ignore-reason.js +48 -0
  92. package/integrations/eslint/plugin/rules/typescript/t004-no-empty-type.js +95 -0
  93. package/integrations/eslint/plugin/rules/typescript/t007-no-fn-in-constructor.js +52 -0
  94. package/integrations/eslint/plugin/rules/typescript/t010-no-nested-union-tuple.js +48 -0
  95. package/integrations/eslint/plugin/rules/typescript/t019-no-this-assign.js +81 -0
  96. package/integrations/eslint/plugin/rules/typescript/t020-no-default-multi-export.js +127 -0
  97. package/integrations/eslint/plugin/rules/typescript/t021-limit-nested-generics.js +150 -0
  98. package/integrations/eslint/test-c041-rule.js +87 -0
  99. package/integrations/eslint/tsconfig.json +27 -0
  100. package/package.json +29 -16
  101. package/rules/README.md +252 -0
  102. package/rules/common/C002_no_duplicate_code/analyzer.js +65 -0
  103. package/rules/common/C002_no_duplicate_code/config.json +23 -0
  104. package/rules/common/C003_no_vague_abbreviations/analyzer.js +418 -0
  105. package/rules/common/C003_no_vague_abbreviations/config.json +35 -0
  106. package/rules/{C006_function_naming → common/C006_function_naming}/analyzer.js +13 -2
  107. package/rules/common/C010_limit_block_nesting/analyzer.js +389 -0
  108. package/rules/common/C013_no_dead_code/analyzer.js +206 -0
  109. package/rules/common/C014_dependency_injection/analyzer.js +338 -0
  110. package/rules/common/C017_constructor_logic/analyzer.js +314 -0
  111. package/rules/{C019_log_level_usage → common/C019_log_level_usage}/analyzer.js +5 -2
  112. package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/analyzer.js +49 -15
  113. package/rules/common/C041_no_sensitive_hardcode/analyzer.js +292 -0
  114. package/rules/common/C042_boolean_name_prefix/analyzer.js +300 -0
  115. package/rules/common/C043_no_console_or_print/analyzer.js +304 -0
  116. package/rules/common/C047_no_duplicate_retry_logic/analyzer.js +351 -0
  117. package/rules/common/C075_explicit_return_types/analyzer.js +103 -0
  118. package/rules/common/C076_single_test_behavior/analyzer.js +121 -0
  119. package/rules/docs/C002_no_duplicate_code.md +57 -0
  120. package/rules/index.js +149 -0
  121. package/rules/migration/converter.js +385 -0
  122. package/rules/migration/mapping.json +164 -0
  123. package/rules/security/S026_json_schema_validation/analyzer.js +251 -0
  124. package/rules/security/S026_json_schema_validation/config.json +27 -0
  125. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +263 -0
  126. package/rules/security/S027_no_hardcoded_secrets/config.json +29 -0
  127. package/rules/security/S029_csrf_protection/analyzer.js +264 -0
  128. package/rules/tests/C002_no_duplicate_code.test.js +50 -0
  129. package/rules/universal/C010/generic.js +0 -0
  130. package/rules/universal/C010/tree-sitter-analyzer.js +0 -0
  131. package/rules/utils/ast-utils.js +191 -0
  132. package/rules/utils/base-analyzer.js +98 -0
  133. package/rules/utils/pattern-matchers.js +239 -0
  134. package/rules/utils/rule-helpers.js +264 -0
  135. package/rules/utils/severity-constants.js +93 -0
  136. package/scripts/build-release.sh +117 -0
  137. package/scripts/ci-report.js +179 -0
  138. package/scripts/install.sh +196 -0
  139. package/scripts/manual-release.sh +338 -0
  140. package/scripts/merge-reports.js +424 -0
  141. package/scripts/pre-release-test.sh +175 -0
  142. package/scripts/prepare-release.sh +202 -0
  143. package/scripts/setup-github-registry.sh +42 -0
  144. package/scripts/test-scripts/README.md +22 -0
  145. package/scripts/test-scripts/test-c041-comparison.js +114 -0
  146. package/scripts/test-scripts/test-c041-eslint.js +67 -0
  147. package/scripts/test-scripts/test-eslint-rules.js +146 -0
  148. package/scripts/test-scripts/test-real-world.js +44 -0
  149. package/scripts/test-scripts/test-rules-on-real-projects.js +86 -0
  150. package/scripts/trigger-release.sh +285 -0
  151. package/scripts/validate-rule-structure.js +148 -0
  152. package/scripts/verify-install.sh +82 -0
  153. package/config/sunlint-schema.json +0 -159
  154. package/config/typescript/custom-rules.js +0 -9
  155. package/config/typescript/package-lock.json +0 -1585
  156. package/config/typescript/package.json +0 -13
  157. package/config/typescript/security-rules/index.js +0 -90
  158. package/config/typescript/tsconfig.json +0 -29
  159. package/core/ai-analyzer.js +0 -169
  160. package/core/eslint-engine-service.js +0 -312
  161. package/core/eslint-instance-manager.js +0 -104
  162. package/core/eslint-integration-service.js +0 -363
  163. package/core/sunlint-engine-service.js +0 -23
  164. package/core/typescript-analyzer.js +0 -262
  165. package/core/typescript-engine.js +0 -313
  166. /package/config/{default.json → defaults/default.json} +0 -0
  167. /package/config/{typescript/eslint.config.js → integrations/eslint/typescript.config.js} +0 -0
  168. /package/config/{typescript/custom-rules-new.js → schemas/sunlint-schema.json} +0 -0
  169. /package/config/{typescript → testing}/test-s005-working.ts +0 -0
  170. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s005-no-origin-auth.js +0 -0
  171. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s006-activation-recovery-secret-not-plaintext.js +0 -0
  172. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s008-crypto-agility.js +0 -0
  173. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s009-no-insecure-crypto.js +0 -0
  174. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s010-no-insecure-random-in-sensitive-context.js +0 -0
  175. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s011-no-insecure-uuid.js +0 -0
  176. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s012-hardcode-secret.js +0 -0
  177. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s014-insecure-tls-version.js +0 -0
  178. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s015-insecure-tls-certificate.js +0 -0
  179. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s016-sensitive-query-parameter.js +0 -0
  180. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s017-no-sql-injection.js +0 -0
  181. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s018-positive-input-validation.js +0 -0
  182. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s019-no-raw-user-input-in-email.js +0 -0
  183. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s020-no-eval-dynamic-execution.js +0 -0
  184. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s022-output-encoding.js +0 -0
  185. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s023-no-json-injection.js +0 -0
  186. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s025-server-side-input-validation.js +0 -0
  187. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s026-json-schema-validation.js +0 -0
  188. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s027-no-hardcoded-secrets.js +0 -0
  189. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s029-require-csrf-protection.js +0 -0
  190. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s030-no-directory-browsing.js +0 -0
  191. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s033-require-samesite-cookie.js +0 -0
  192. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s034-require-host-cookie-prefix.js +0 -0
  193. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s035-cookie-specific-path.js +0 -0
  194. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s036-no-unsafe-file-include.js +0 -0
  195. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s037-require-anti-cache-headers.js +0 -0
  196. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s038-no-version-disclosure.js +0 -0
  197. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s039-no-session-token-in-url.js +0 -0
  198. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s041-require-session-invalidate-on-logout.js +0 -0
  199. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s042-require-periodic-reauthentication.js +0 -0
  200. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s043-terminate-sessions-on-password-change.js +0 -0
  201. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s044-require-full-session-for-sensitive-operations.js +0 -0
  202. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s045-anti-automation-controls.js +0 -0
  203. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s046-secure-notification-on-auth-change.js +0 -0
  204. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s048-password-credential-recovery.js +0 -0
  205. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s050-session-token-weak-hash.js +0 -0
  206. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s052-secure-random-authentication-code.js +0 -0
  207. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s054-verification-default-account.js +0 -0
  208. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s057-utc-logging.js +0 -0
  209. /package/{config/typescript/security-rules → integrations/eslint/plugin/rules/security}/s058-no-ssrf.js +0 -0
  210. /package/rules/{C006_function_naming → common/C006_function_naming}/config.json +0 -0
  211. /package/rules/{C019_log_level_usage → common/C019_log_level_usage}/config.json +0 -0
  212. /package/rules/{C029_catch_block_logging → common/C029_catch_block_logging}/config.json +0 -0
  213. /package/rules/{C031_validation_separation → common/C031_validation_separation}/analyzer.js +0 -0
  214. /package/rules/{C031_validation_separation/README.md → docs/C031_validation_separation.md} +0 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Custom ESLint rule for: T025 – Avoid deeply nested union or tuple types
3
+ * Rule ID: custom/t025
4
+ * Purpose: Prevent complex nested union or tuple types to maintain type readability and simplicity
5
+ */
6
+
7
+ module.exports = {
8
+ meta: {
9
+ type: "suggestion",
10
+ docs: {
11
+ description: "Avoid deeply nested union or tuple types",
12
+ recommended: false
13
+ },
14
+ schema: [],
15
+ messages: {
16
+ nestedUnion: "Avoid nested union types.",
17
+ nestedTuple: "Avoid nested tuple types."
18
+ }
19
+ },
20
+ create(context) {
21
+ function checkNestedTypes(node) {
22
+ if (node.type === 'TSUnionType') {
23
+ const nested = node.types.some(t => t.type === 'TSUnionType' || t.type === 'TSTupleType');
24
+ if (nested) {
25
+ context.report({
26
+ node,
27
+ messageId: "nestedUnion"
28
+ });
29
+ }
30
+ }
31
+
32
+ if (node.type === 'TSTupleType') {
33
+ const nested = node.elementTypes.some(t => t.type === 'TSTupleType' || t.type === 'TSUnionType');
34
+ if (nested) {
35
+ context.report({
36
+ node,
37
+ messageId: "nestedTuple"
38
+ });
39
+ }
40
+ }
41
+ }
42
+
43
+ return {
44
+ TSUnionType: checkNestedTypes,
45
+ TSTupleType: checkNestedTypes
46
+ };
47
+ }
48
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview Rule T019: Do not assign to this arbitrarily
3
+ * @description Maintain proper context and avoid this manipulation
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'problem',
9
+ docs: {
10
+ description: 'Do not assign to this arbitrarily',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: []
16
+ },
17
+
18
+ create(context) {
19
+ return {
20
+ // Detect direct assignment to 'this'
21
+ AssignmentExpression(node) {
22
+ if (node.left.type === 'ThisExpression') {
23
+ context.report({
24
+ node,
25
+ message: 'T019: Do not assign to "this" directly. Use proper binding, arrow functions, or explicit parameter passing.'
26
+ });
27
+ }
28
+ },
29
+
30
+ // Detect patterns like "const that = this" or "var self = this"
31
+ VariableDeclarator(node) {
32
+ if (node.init && node.init.type === 'ThisExpression') {
33
+ // Common patterns: that, self, _this, me
34
+ const variableName = node.id.name;
35
+ const suspiciousNames = ['that', 'self', '_this', 'me', '_self'];
36
+
37
+ if (suspiciousNames.includes(variableName.toLowerCase())) {
38
+ context.report({
39
+ node,
40
+ message: `T019: Avoid storing "this" in variable "${variableName}". Use arrow functions or proper binding instead.`
41
+ });
42
+ }
43
+ }
44
+ },
45
+
46
+ // Detect method calls that bind this arbitrarily
47
+ CallExpression(node) {
48
+ // Detect .bind(this) patterns that might be problematic
49
+ if (node.callee.type === 'MemberExpression' &&
50
+ node.callee.property.name === 'bind' &&
51
+ node.arguments.length > 0) {
52
+
53
+ const bindTarget = node.arguments[0];
54
+ if (bindTarget.type === 'ThisExpression') {
55
+ // Allow legitimate .bind(this) in constructors and specific patterns
56
+ const sourceCode = context.sourceCode || context.getSourceCode();
57
+ let currentScope = sourceCode.getScope ? sourceCode.getScope(node) : null;
58
+
59
+ if (!currentScope && context.getScope) {
60
+ currentScope = context.getScope();
61
+ }
62
+
63
+ const parent = currentScope ? currentScope.block : null;
64
+ const isInConstructor = parent &&
65
+ parent.type === 'FunctionExpression' &&
66
+ parent.parent &&
67
+ parent.parent.key &&
68
+ parent.parent.key.name === 'constructor';
69
+
70
+ if (!isInConstructor) {
71
+ context.report({
72
+ node,
73
+ message: 'T019: Consider using arrow functions instead of .bind(this) for cleaner context handling.'
74
+ });
75
+ }
76
+ }
77
+ }
78
+ }
79
+ };
80
+ }
81
+ };
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @fileoverview Rule T020: Avoid export default for multi-responsibility modules
3
+ * @description Improve tree-shaking and module clarity
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Avoid export default for multi-responsibility modules',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: []
16
+ },
17
+
18
+ create(context) {
19
+ let hasDefaultExport = false;
20
+ let namedExports = [];
21
+ let exportedFunctions = [];
22
+ let exportedClasses = [];
23
+ let exportedConstants = [];
24
+
25
+ function analyzeExport(node, exportType) {
26
+ if (exportType === 'default') {
27
+ hasDefaultExport = true;
28
+ } else {
29
+ namedExports.push(node);
30
+ }
31
+ }
32
+
33
+ return {
34
+ // Track export default statements
35
+ ExportDefaultDeclaration(node) {
36
+ hasDefaultExport = true;
37
+
38
+ // Analyze what's being exported as default
39
+ if (node.declaration) {
40
+ if (node.declaration.type === 'FunctionDeclaration') {
41
+ exportedFunctions.push(node.declaration);
42
+ } else if (node.declaration.type === 'ClassDeclaration') {
43
+ exportedClasses.push(node.declaration);
44
+ }
45
+ }
46
+ },
47
+
48
+ // Track named exports
49
+ ExportNamedDeclaration(node) {
50
+ namedExports.push(node);
51
+
52
+ if (node.declaration) {
53
+ if (node.declaration.type === 'FunctionDeclaration') {
54
+ exportedFunctions.push(node.declaration);
55
+ } else if (node.declaration.type === 'ClassDeclaration') {
56
+ exportedClasses.push(node.declaration);
57
+ } else if (node.declaration.type === 'VariableDeclaration') {
58
+ exportedConstants.push(node.declaration);
59
+ }
60
+ }
61
+
62
+ if (node.specifiers) {
63
+ node.specifiers.forEach(spec => {
64
+ if (spec.type === 'ExportSpecifier') {
65
+ namedExports.push(spec);
66
+ }
67
+ });
68
+ }
69
+ },
70
+
71
+ // Check at the end of the program
72
+ 'Program:exit'() {
73
+ if (!hasDefaultExport) {
74
+ return; // No default export, rule doesn't apply
75
+ }
76
+
77
+ // Count total exports
78
+ const totalNamedExports = namedExports.length;
79
+ const totalFunctions = exportedFunctions.length;
80
+ const totalClasses = exportedClasses.length;
81
+ const totalConstants = exportedConstants.length;
82
+
83
+ // Multi-responsibility indicators:
84
+ // 1. Multiple named exports alongside default export
85
+ // 2. Multiple functions being exported
86
+ // 3. Multiple classes being exported
87
+ // 4. Mix of functions, classes, and constants
88
+
89
+ const isMultiResponsibility =
90
+ totalNamedExports > 0 || // Has both default and named exports
91
+ totalFunctions > 1 || // Multiple functions
92
+ totalClasses > 1 || // Multiple classes
93
+ (totalFunctions > 0 && totalClasses > 0) || // Mix of functions and classes
94
+ (totalConstants > 2); // Too many constants
95
+
96
+ if (isMultiResponsibility) {
97
+ // Find the default export node to report
98
+ const sourceCode = context.getSourceCode();
99
+ const defaultExportNode = sourceCode.ast.body.find(
100
+ node => node.type === 'ExportDefaultDeclaration'
101
+ );
102
+
103
+ if (defaultExportNode) {
104
+ let message = 'T020: Avoid export default for multi-responsibility modules. ';
105
+
106
+ if (totalNamedExports > 0) {
107
+ message += `Found ${totalNamedExports} named exports alongside default export. `;
108
+ }
109
+ if (totalFunctions > 1) {
110
+ message += `Found ${totalFunctions} exported functions. `;
111
+ }
112
+ if (totalClasses > 1) {
113
+ message += `Found ${totalClasses} exported classes. `;
114
+ }
115
+
116
+ message += 'Consider using only named exports for better tree-shaking and clarity.';
117
+
118
+ context.report({
119
+ node: defaultExportNode,
120
+ message
121
+ });
122
+ }
123
+ }
124
+ }
125
+ };
126
+ }
127
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @fileoverview Rule T021: Limit deeply nested generics
3
+ * @description Improve code readability and TypeScript performance
4
+ */
5
+
6
+ module.exports = {
7
+ meta: {
8
+ type: 'suggestion',
9
+ docs: {
10
+ description: 'Limit deeply nested generics',
11
+ category: 'Best Practices',
12
+ recommended: true
13
+ },
14
+ fixable: null,
15
+ schema: [
16
+ {
17
+ type: 'object',
18
+ properties: {
19
+ maxDepth: {
20
+ type: 'integer',
21
+ minimum: 1,
22
+ default: 3
23
+ }
24
+ },
25
+ additionalProperties: false
26
+ }
27
+ ]
28
+ },
29
+
30
+ create(context) {
31
+ const options = context.options[0] || {};
32
+ const maxDepth = options.maxDepth || 3;
33
+
34
+ function getGenericDepth(typeNode) {
35
+ if (!typeNode) return 0;
36
+
37
+ switch (typeNode.type) {
38
+ case 'TSTypeReference':
39
+ // Generic types like Array<T>, Promise<U>, etc.
40
+ // ESLint uses 'typeArguments' instead of 'typeParameters'
41
+ if (typeNode.typeArguments && typeNode.typeArguments.params) {
42
+ let maxChildDepth = 0;
43
+ for (const param of typeNode.typeArguments.params) {
44
+ const childDepth = getGenericDepth(param);
45
+ maxChildDepth = Math.max(maxChildDepth, childDepth);
46
+ }
47
+ return 1 + maxChildDepth;
48
+ }
49
+ return 0;
50
+
51
+ case 'TSArrayType':
52
+ // Array types like T[]
53
+ return 1 + getGenericDepth(typeNode.elementType);
54
+
55
+ case 'TSUnionType':
56
+ case 'TSIntersectionType':
57
+ // Union types like A | B or intersection types A & B
58
+ let maxDepth = 0;
59
+ for (const typeItem of typeNode.types) {
60
+ maxDepth = Math.max(maxDepth, getGenericDepth(typeItem));
61
+ }
62
+ return maxDepth;
63
+
64
+ case 'TSTupleType':
65
+ // Tuple types like [A, B, C]
66
+ let maxTupleDepth = 0;
67
+ for (const elementType of typeNode.elementTypes) {
68
+ maxTupleDepth = Math.max(maxTupleDepth, getGenericDepth(elementType));
69
+ }
70
+ return 1 + maxTupleDepth;
71
+
72
+ case 'TSMappedType':
73
+ // Mapped types like { [K in keyof T]: U }
74
+ return 1 + getGenericDepth(typeNode.typeAnnotation);
75
+
76
+ case 'TSConditionalType':
77
+ // Conditional types like T extends U ? A : B
78
+ return 1 + Math.max(
79
+ getGenericDepth(typeNode.trueType),
80
+ getGenericDepth(typeNode.falseType)
81
+ );
82
+
83
+ default:
84
+ return 0;
85
+ }
86
+ }
87
+
88
+ function checkTypeDepth(node, typeName) {
89
+ const depth = getGenericDepth(node);
90
+ if (depth > maxDepth) {
91
+ context.report({
92
+ node,
93
+ message: `T021: Generic nesting depth of ${depth} exceeds maximum of ${maxDepth}. Consider breaking down complex types into intermediate type aliases for better readability and TypeScript compiler performance.`
94
+ });
95
+ }
96
+ }
97
+
98
+ return {
99
+ // Check type aliases - this is the main entry point
100
+ TSTypeAliasDeclaration(node) {
101
+ checkTypeDepth(node.typeAnnotation, node.id.name);
102
+ },
103
+
104
+ // Check interface properties
105
+ TSPropertySignature(node) {
106
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
107
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'property');
108
+ }
109
+ },
110
+
111
+ // Check function parameters
112
+ TSParameterProperty(node) {
113
+ if (node.parameter && node.parameter.typeAnnotation) {
114
+ checkTypeDepth(node.parameter.typeAnnotation.typeAnnotation, 'parameter');
115
+ }
116
+ },
117
+
118
+ // Check function return types
119
+ TSFunctionType(node) {
120
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
121
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'return type');
122
+ }
123
+ },
124
+
125
+ // Check variable declarations
126
+ VariableDeclarator(node) {
127
+ if (node.id && node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
128
+ checkTypeDepth(node.id.typeAnnotation.typeAnnotation, 'variable');
129
+ }
130
+ },
131
+
132
+ // Check generic constraints
133
+ TSTypeParameter(node) {
134
+ if (node.constraint) {
135
+ checkTypeDepth(node.constraint, 'generic constraint');
136
+ }
137
+ if (node.default) {
138
+ checkTypeDepth(node.default, 'generic default');
139
+ }
140
+ },
141
+
142
+ // Check method signatures
143
+ TSMethodSignature(node) {
144
+ if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
145
+ checkTypeDepth(node.typeAnnotation.typeAnnotation, 'method return type');
146
+ }
147
+ }
148
+ };
149
+ }
150
+ };
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ const rule = require('./plugin/rules/common/c041-no-config-inline.js');
4
+
5
+ // Simple test cases
6
+ const testCases = [
7
+ // Should NOT flag (false positives to avoid)
8
+ { code: 'const inputType = "password";', shouldError: false, desc: 'InputType definition' },
9
+ { code: 'const fieldType = "password";', shouldError: false, desc: 'Field type' },
10
+ { code: 'const usePassword = "password_validation";', shouldError: false, desc: 'Hook usage' },
11
+ { code: 'const testPassword = "test123";', shouldError: false, desc: 'Test data' },
12
+ { code: 'const mockSecret = "mock_secret_key";', shouldError: false, desc: 'Mock data' },
13
+ { code: 'const route = "/reset_password";', shouldError: false, desc: 'Route path' },
14
+
15
+ // SHOULD flag (real sensitive data)
16
+ { code: 'const dbPassword = "MySecretP@ssw0rd123";', shouldError: true, desc: 'Real hardcoded password' },
17
+ { code: 'const apiKey = "sk-1234567890abcdef1234567890abcdef";', shouldError: true, desc: 'Real API key' },
18
+ { code: 'const authToken = "bearer_token_12345678901234567";', shouldError: true, desc: 'Real auth token' }
19
+ ];
20
+
21
+ console.log('🧪 Testing ESLint C041 rule manually...\n');
22
+
23
+ let passed = 0;
24
+ let failed = 0;
25
+
26
+ // Mock context for testing
27
+ function createMockContext(code) {
28
+ return {
29
+ getSourceCode: () => ({
30
+ lines: code.split('\n')
31
+ }),
32
+ report: ({ node, messageId }) => {
33
+ return { reported: true, messageId };
34
+ }
35
+ };
36
+ }
37
+
38
+ testCases.forEach((testCase, index) => {
39
+ try {
40
+ let reported = false;
41
+
42
+ // Parse the code to extract literal nodes (simplified)
43
+ const literalMatch = testCase.code.match(/"([^"]+)"/);
44
+ if (literalMatch) {
45
+ const mockNode = {
46
+ value: literalMatch[1],
47
+ loc: { start: { line: 1 } }
48
+ };
49
+
50
+ const mockContext = {
51
+ getSourceCode: () => ({
52
+ lines: [testCase.code]
53
+ }),
54
+ report: ({ node, messageId }) => {
55
+ reported = true;
56
+ }
57
+ };
58
+
59
+ const ruleImplementation = rule.create(mockContext);
60
+ ruleImplementation.Literal(mockNode);
61
+ }
62
+
63
+ const testPassed = reported === testCase.shouldError;
64
+
65
+ if (testPassed) {
66
+ console.log(`✅ Test ${index + 1}: ${testCase.desc}`);
67
+ passed++;
68
+ } else {
69
+ console.log(`❌ Test ${index + 1}: ${testCase.desc}`);
70
+ console.log(` Expected: ${testCase.shouldError ? 'Should flag' : 'Should NOT flag'}`);
71
+ console.log(` Actual: ${reported ? 'Flagged' : 'Not flagged'}`);
72
+ failed++;
73
+ }
74
+ } catch (error) {
75
+ console.log(`💥 Test ${index + 1}: ${testCase.desc} - Error: ${error.message}`);
76
+ failed++;
77
+ }
78
+ });
79
+
80
+ console.log(`\n📊 Test Results: ${passed} passed, ${failed} failed`);
81
+ console.log(`Success rate: ${Math.round((passed / (passed + failed)) * 100)}%`);
82
+
83
+ if (failed === 0) {
84
+ console.log('\n🎉 All ESLint rule tests passed!');
85
+ } else {
86
+ console.log('\n⚠️ Some tests failed. Review the rule logic.');
87
+ }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.0.7",
3
+ "version": "1.1.3",
4
4
  "description": "☀️ Sun Lint - Universal Coding Standards | Multi-rule Quality & Security Analysis with ESLint Integration",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -16,18 +16,21 @@
16
16
  "access": "public"
17
17
  },
18
18
  "scripts": {
19
- "test": "npm run test:unit && npm run test:integration",
20
- "test:unit": "node test/unit/test-runner.js",
21
- "test:integration": "node test/integration/test-runner.js",
22
- "test:c019": "node cli.js --rule=C019 --input=test/fixtures --format=eslint",
23
- "test:c006": "node cli.js --rule=C006 --input=test/fixtures --format=eslint",
24
- "test:c029": "node cli.js --rule=C029 --input=test/fixtures/c029 --format=eslint",
25
- "test:multi": "node cli.js --rules=C019,C006,C029 --input=test/fixtures --format=eslint",
26
- "test:all": "node cli.js --all --input=test/fixtures --format=eslint",
27
- "test:quality": "node cli.js --category=quality --input=test/fixtures --format=eslint",
28
- "test:security": "node cli.js --category=security --input=test/fixtures --format=eslint",
19
+ "test": "node examples/run-tests.js",
20
+ "test:examples": "node examples/integration-tests/examples-integration-test.js",
21
+ "test:architecture": "node examples/integration-tests/core-architecture-test.js",
22
+ "test:integration": "echo 'Temporarily disabled - config extension issue'",
23
+ "test:realworld": "node examples/integration-tests/realworld-integration-test.js",
24
+ "test:cli": "node examples/integration-tests/direct-cli-test.js",
25
+ "test:c019": "node cli.js --rule=C019 --input=examples/test-fixtures --format=eslint",
26
+ "test:c006": "node cli.js --rule=C006 --input=examples/test-fixtures --format=eslint",
27
+ "test:c029": "node cli.js --rule=C029 --input=examples/test-fixtures --format=eslint",
28
+ "test:multi": "node cli.js --rules=C019,C006,C029 --input=examples/test-fixtures --format=eslint",
29
+ "test:all": "node cli.js --all --input=examples/test-fixtures --format=eslint",
30
+ "test:quality": "node cli.js --category=quality --input=examples/test-fixtures --format=eslint",
31
+ "test:security": "node cli.js --category=security --input=examples/test-fixtures --format=eslint",
29
32
  "demo": "./demo.sh",
30
- "demo:single": "node cli.js --rule=C019 --input=test/fixtures/typescript --format=eslint",
33
+ "demo:single": "node cli.js --rule=C019 --input=examples/test-fixtures/typescript --format=eslint",
31
34
  "demo:multi": "node cli.js --rules=C019,C006 --input=test/fixtures --format=summary",
32
35
  "demo:quality": "node cli.js --quality --input=test/fixtures --format=summary",
33
36
  "demo:security": "node cli.js --security --input=test/fixtures --format=summary",
@@ -68,19 +71,26 @@
68
71
  "core/",
69
72
  "rules/",
70
73
  "config/",
74
+ "engines/",
75
+ "integrations/",
76
+ "scripts/",
77
+ "docs/",
78
+ ".sunlint.json",
71
79
  "README.md",
72
80
  "LICENSE",
73
- "CHANGELOG.md"
81
+ "CHANGELOG.md",
82
+ "PROJECT_STRUCTURE.md",
83
+ "CONTRIBUTING.md"
74
84
  ],
75
85
  "engines": {
76
86
  "node": ">=18.18.0"
77
87
  },
78
88
  "dependencies": {
79
- "@typescript-eslint/eslint-plugin": "^8.36.0",
80
- "@typescript-eslint/parser": "^8.36.0",
89
+ "@babel/parser": "^7.23.0",
90
+ "@typescript-eslint/parser": "^8.38.0",
81
91
  "chalk": "^4.1.2",
82
92
  "commander": "^12.1.0",
83
- "eslint": "^9.30.0",
93
+ "espree": "^9.6.0",
84
94
  "glob": "^11.0.0",
85
95
  "minimatch": "^10.0.3",
86
96
  "node-fetch": "^3.3.2",
@@ -89,6 +99,9 @@
89
99
  },
90
100
  "devDependencies": {
91
101
  "@types/node": "^22.10.1",
102
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
103
+ "@typescript-eslint/parser": "^8.38.0",
104
+ "eslint": "^9.31.0",
92
105
  "jest": "^29.7.0"
93
106
  },
94
107
  "bugs": {