code-auditor-mcp 3.1.0 → 3.4.0

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 (360) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +665 -1
  3. package/README.md +85 -11
  4. package/dist/__test_tmp__/spec18_test.d.ts +2 -0
  5. package/dist/__test_tmp__/spec18_test.d.ts.map +1 -0
  6. package/dist/__test_tmp__/spec18_test.js +5 -0
  7. package/dist/__test_tmp__/spec18_test.js.map +1 -0
  8. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts +6 -0
  9. package/dist/__tests__/fixtures/spec-20/scripts/tool.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/spec-20/scripts/tool.js +12 -0
  11. package/dist/__tests__/fixtures/spec-20/scripts/tool.js.map +1 -0
  12. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts +7 -0
  13. package/dist/__tests__/fixtures/spec-20/src/demo-module.d.ts.map +1 -0
  14. package/dist/__tests__/fixtures/spec-20/src/demo-module.js +13 -0
  15. package/dist/__tests__/fixtures/spec-20/src/demo-module.js.map +1 -0
  16. package/dist/analyzers/analyzerUtils.js +2 -2
  17. package/dist/analyzers/analyzerUtils.js.map +1 -1
  18. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +1 -1
  19. package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
  20. package/dist/analyzers/cross-language/APIContractAnalyzer.js +2 -4
  21. package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
  22. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts +3 -0
  23. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.d.ts.map +1 -1
  24. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js +3 -5
  25. package/dist/analyzers/cross-language/CrossLanguageSOLIDAnalyzer.js.map +1 -1
  26. package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
  27. package/dist/analyzers/cross-language/DependencyGraphBuilder.js +0 -3
  28. package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
  29. package/dist/analyzers/cross-language/SchemaValidator.d.ts +1 -1
  30. package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
  31. package/dist/analyzers/cross-language/SchemaValidator.js +2 -5
  32. package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
  33. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +83 -0
  34. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -0
  35. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +611 -0
  36. package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -0
  37. package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
  38. package/dist/analyzers/documentationAnalyzer.js +8 -4
  39. package/dist/analyzers/documentationAnalyzer.js.map +1 -1
  40. package/dist/analyzers/orm/adapterRegistry.d.ts +41 -0
  41. package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -0
  42. package/dist/analyzers/orm/adapterRegistry.js +73 -0
  43. package/dist/analyzers/orm/adapterRegistry.js.map +1 -0
  44. package/dist/analyzers/orm/drizzleAdapter.d.ts +42 -0
  45. package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -0
  46. package/dist/analyzers/orm/drizzleAdapter.js +166 -0
  47. package/dist/analyzers/orm/drizzleAdapter.js.map +1 -0
  48. package/dist/analyzers/orm/index.d.ts +14 -0
  49. package/dist/analyzers/orm/index.d.ts.map +1 -0
  50. package/dist/analyzers/orm/index.js +24 -0
  51. package/dist/analyzers/orm/index.js.map +1 -0
  52. package/dist/analyzers/orm/prismaAdapter.d.ts +45 -0
  53. package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -0
  54. package/dist/analyzers/orm/prismaAdapter.js +170 -0
  55. package/dist/analyzers/orm/prismaAdapter.js.map +1 -0
  56. package/dist/analyzers/orm/types.d.ts +63 -0
  57. package/dist/analyzers/orm/types.d.ts.map +1 -0
  58. package/dist/analyzers/orm/types.js +10 -0
  59. package/dist/analyzers/orm/types.js.map +1 -0
  60. package/dist/analyzers/provenance.d.ts +133 -0
  61. package/dist/analyzers/provenance.d.ts.map +1 -0
  62. package/dist/analyzers/provenance.js +1041 -0
  63. package/dist/analyzers/provenance.js.map +1 -0
  64. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  65. package/dist/analyzers/reactAnalyzer.js +117 -1
  66. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  67. package/dist/analyzers/ruleRegistry.d.ts +38 -0
  68. package/dist/analyzers/ruleRegistry.d.ts.map +1 -0
  69. package/dist/analyzers/ruleRegistry.js +123 -0
  70. package/dist/analyzers/ruleRegistry.js.map +1 -0
  71. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +58 -0
  72. package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -0
  73. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +445 -0
  74. package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -0
  75. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +52 -0
  76. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  77. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +108 -26
  78. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  79. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +64 -0
  80. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
  81. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +259 -103
  82. package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
  83. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
  84. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +5 -15
  85. package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
  86. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
  87. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +10 -10
  88. package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
  89. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +25 -0
  90. package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
  91. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +250 -59
  92. package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
  93. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +103 -0
  94. package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -0
  95. package/dist/analyzers/universal/UniversalStylesAnalyzer.js +750 -0
  96. package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -0
  97. package/dist/auditRunner.d.ts.map +1 -1
  98. package/dist/auditRunner.js +483 -3
  99. package/dist/auditRunner.js.map +1 -1
  100. package/dist/baseline.d.ts +98 -0
  101. package/dist/baseline.d.ts.map +1 -0
  102. package/dist/baseline.js +184 -0
  103. package/dist/baseline.js.map +1 -0
  104. package/dist/churn/churnExtractor.d.ts +32 -0
  105. package/dist/churn/churnExtractor.d.ts.map +1 -0
  106. package/dist/churn/churnExtractor.js +261 -0
  107. package/dist/churn/churnExtractor.js.map +1 -0
  108. package/dist/cli.js +1215 -11
  109. package/dist/cli.js.map +1 -1
  110. package/dist/codeIndexDB-enhanced.js +1 -1
  111. package/dist/codeIndexDB-enhanced.js.map +1 -1
  112. package/dist/codeIndexDB.d.ts +101 -0
  113. package/dist/codeIndexDB.d.ts.map +1 -1
  114. package/dist/codeIndexDB.js +661 -5
  115. package/dist/codeIndexDB.js.map +1 -1
  116. package/dist/config/configLoader.d.ts.map +1 -1
  117. package/dist/config/configLoader.js +95 -1
  118. package/dist/config/configLoader.js.map +1 -1
  119. package/dist/config/defaults.d.ts +72 -1
  120. package/dist/config/defaults.d.ts.map +1 -1
  121. package/dist/config/defaults.js +146 -15
  122. package/dist/config/defaults.js.map +1 -1
  123. package/dist/config/pathProfiles.d.ts +38 -0
  124. package/dist/config/pathProfiles.d.ts.map +1 -0
  125. package/dist/config/pathProfiles.js +47 -0
  126. package/dist/config/pathProfiles.js.map +1 -0
  127. package/dist/conventions/conventionMiner.d.ts +63 -0
  128. package/dist/conventions/conventionMiner.d.ts.map +1 -0
  129. package/dist/conventions/conventionMiner.js +696 -0
  130. package/dist/conventions/conventionMiner.js.map +1 -0
  131. package/dist/coverage/coverageService.d.ts +49 -0
  132. package/dist/coverage/coverageService.d.ts.map +1 -0
  133. package/dist/coverage/coverageService.js +117 -0
  134. package/dist/coverage/coverageService.js.map +1 -0
  135. package/dist/coverage/istanbulParser.d.ts +84 -0
  136. package/dist/coverage/istanbulParser.d.ts.map +1 -0
  137. package/dist/coverage/istanbulParser.js +120 -0
  138. package/dist/coverage/istanbulParser.js.map +1 -0
  139. package/dist/coverage/lcovParser.d.ts +40 -0
  140. package/dist/coverage/lcovParser.d.ts.map +1 -0
  141. package/dist/coverage/lcovParser.js +168 -0
  142. package/dist/coverage/lcovParser.js.map +1 -0
  143. package/dist/fingerprint.d.ts +17 -0
  144. package/dist/fingerprint.d.ts.map +1 -1
  145. package/dist/fingerprint.js +42 -0
  146. package/dist/fingerprint.js.map +1 -1
  147. package/dist/grammars/tree-sitter-css.wasm +0 -0
  148. package/dist/graph/blastRadius.d.ts +27 -0
  149. package/dist/graph/blastRadius.d.ts.map +1 -0
  150. package/dist/graph/blastRadius.js +150 -0
  151. package/dist/graph/blastRadius.js.map +1 -0
  152. package/dist/graph/callGraph.d.ts +78 -0
  153. package/dist/graph/callGraph.d.ts.map +1 -0
  154. package/dist/graph/callGraph.js +484 -0
  155. package/dist/graph/callGraph.js.map +1 -0
  156. package/dist/graph/importGraph.d.ts +91 -0
  157. package/dist/graph/importGraph.d.ts.map +1 -0
  158. package/dist/graph/importGraph.js +602 -0
  159. package/dist/graph/importGraph.js.map +1 -0
  160. package/dist/graph/outputFormatter.d.ts +55 -0
  161. package/dist/graph/outputFormatter.d.ts.map +1 -0
  162. package/dist/graph/outputFormatter.js +283 -0
  163. package/dist/graph/outputFormatter.js.map +1 -0
  164. package/dist/hooks/codex.d.ts +36 -1
  165. package/dist/hooks/codex.d.ts.map +1 -1
  166. package/dist/hooks/codex.js +88 -54
  167. package/dist/hooks/codex.js.map +1 -1
  168. package/dist/hotspots/hotspotScorer.d.ts +20 -0
  169. package/dist/hotspots/hotspotScorer.d.ts.map +1 -0
  170. package/dist/hotspots/hotspotScorer.js +202 -0
  171. package/dist/hotspots/hotspotScorer.js.map +1 -0
  172. package/dist/invariants/invariant-rules.schema.json +42 -1
  173. package/dist/invariants/ruleEngine.d.ts +4 -1
  174. package/dist/invariants/ruleEngine.d.ts.map +1 -1
  175. package/dist/invariants/ruleEngine.js +141 -5
  176. package/dist/invariants/ruleEngine.js.map +1 -1
  177. package/dist/invariants/ruleValidator.d.ts.map +1 -1
  178. package/dist/invariants/ruleValidator.js +62 -1
  179. package/dist/invariants/ruleValidator.js.map +1 -1
  180. package/dist/invariants/types.d.ts +23 -2
  181. package/dist/invariants/types.d.ts.map +1 -1
  182. package/dist/languages/UniversalAnalyzer.d.ts +1 -1
  183. package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
  184. package/dist/languages/UniversalAnalyzer.js +59 -24
  185. package/dist/languages/UniversalAnalyzer.js.map +1 -1
  186. package/dist/languages/index.d.ts +1 -0
  187. package/dist/languages/index.d.ts.map +1 -1
  188. package/dist/languages/index.js +3 -0
  189. package/dist/languages/index.js.map +1 -1
  190. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +59 -0
  191. package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -0
  192. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +236 -0
  193. package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -0
  194. package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
  195. package/dist/languages/tree-sitter/parser.js +2 -0
  196. package/dist/languages/tree-sitter/parser.js.map +1 -1
  197. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  198. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +61 -10
  199. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  200. package/dist/ledger.d.ts +128 -0
  201. package/dist/ledger.d.ts.map +1 -0
  202. package/dist/ledger.js +345 -0
  203. package/dist/ledger.js.map +1 -0
  204. package/dist/mcp-tools/projectTasks.d.ts.map +1 -1
  205. package/dist/mcp-tools/projectTasks.js +9 -14
  206. package/dist/mcp-tools/projectTasks.js.map +1 -1
  207. package/dist/mcp.js +0 -0
  208. package/dist/mcpAuditJobs.d.ts.map +1 -1
  209. package/dist/mcpAuditJobs.js +3 -0
  210. package/dist/mcpAuditJobs.js.map +1 -1
  211. package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
  212. package/dist/reporting/csvReportGenerator.js +7 -1
  213. package/dist/reporting/csvReportGenerator.js.map +1 -1
  214. package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
  215. package/dist/reporting/htmlReportGenerator.js +5 -1
  216. package/dist/reporting/htmlReportGenerator.js.map +1 -1
  217. package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
  218. package/dist/reporting/jsonReportGenerator.js +10 -2
  219. package/dist/reporting/jsonReportGenerator.js.map +1 -1
  220. package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
  221. package/dist/reporting/sarifReportGenerator.js +17 -17
  222. package/dist/reporting/sarifReportGenerator.js.map +1 -1
  223. package/dist/scripts/runBench.d.ts +94 -0
  224. package/dist/scripts/runBench.d.ts.map +1 -0
  225. package/dist/scripts/runBench.js +1559 -0
  226. package/dist/scripts/runBench.js.map +1 -0
  227. package/dist/search/QueryParser.d.ts.map +1 -1
  228. package/dist/search/QueryParser.js +51 -4
  229. package/dist/search/QueryParser.js.map +1 -1
  230. package/dist/services/CodeMapGenerator.d.ts +33 -0
  231. package/dist/services/CodeMapGenerator.d.ts.map +1 -1
  232. package/dist/services/CodeMapGenerator.js +404 -0
  233. package/dist/services/CodeMapGenerator.js.map +1 -1
  234. package/dist/styles/normalizer.d.ts +37 -0
  235. package/dist/styles/normalizer.d.ts.map +1 -0
  236. package/dist/styles/normalizer.js +301 -0
  237. package/dist/styles/normalizer.js.map +1 -0
  238. package/dist/styles/styleExtractor.d.ts +38 -0
  239. package/dist/styles/styleExtractor.d.ts.map +1 -0
  240. package/dist/styles/styleExtractor.js +630 -0
  241. package/dist/styles/styleExtractor.js.map +1 -0
  242. package/dist/styles/styleIndexer.d.ts +35 -0
  243. package/dist/styles/styleIndexer.d.ts.map +1 -0
  244. package/dist/styles/styleIndexer.js +266 -0
  245. package/dist/styles/styleIndexer.js.map +1 -0
  246. package/dist/styles/tailwindConfigLoader.d.ts +38 -0
  247. package/dist/styles/tailwindConfigLoader.d.ts.map +1 -0
  248. package/dist/styles/tailwindConfigLoader.js +374 -0
  249. package/dist/styles/tailwindConfigLoader.js.map +1 -0
  250. package/dist/styles/tailwindExpander.d.ts +38 -0
  251. package/dist/styles/tailwindExpander.d.ts.map +1 -0
  252. package/dist/styles/tailwindExpander.js +637 -0
  253. package/dist/styles/tailwindExpander.js.map +1 -0
  254. package/dist/styles/types.d.ts +92 -0
  255. package/dist/styles/types.d.ts.map +1 -0
  256. package/dist/styles/types.js +10 -0
  257. package/dist/styles/types.js.map +1 -0
  258. package/dist/symbols.d.ts +21 -0
  259. package/dist/symbols.d.ts.map +1 -0
  260. package/dist/symbols.js +31 -0
  261. package/dist/symbols.js.map +1 -0
  262. package/dist/types.d.ts +326 -3
  263. package/dist/types.d.ts.map +1 -1
  264. package/dist/types.js.map +1 -1
  265. package/package.json +9 -5
  266. package/plugin/.claude-plugin/plugin.json +1 -1
  267. package/plugin/scripts/hook-audit.sh +5 -1
  268. package/plugin/skills/code-auditor/SKILL.md +51 -0
  269. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts +0 -21
  270. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.d.ts.map +0 -1
  271. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js +0 -21
  272. package/dist/analyzers/__tests__/fixtures/spec-17/anonymous-callback-map.js.map +0 -1
  273. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts +0 -13
  274. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.d.ts.map +0 -1
  275. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js +0 -12
  276. package/dist/analyzers/__tests__/fixtures/spec-17/barrel-test-migration.js.map +0 -1
  277. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts +0 -11
  278. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.d.ts.map +0 -1
  279. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js +0 -22
  280. package/dist/analyzers/__tests__/fixtures/spec-17/direct-access-allow-config.js.map +0 -1
  281. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts +0 -9
  282. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.d.ts.map +0 -1
  283. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js +0 -13
  284. package/dist/analyzers/__tests__/fixtures/spec-17/exported-undocumented.js.map +0 -1
  285. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts +0 -13
  286. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.d.ts.map +0 -1
  287. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js +0 -18
  288. package/dist/analyzers/__tests__/fixtures/spec-17/for-loop-query.js.map +0 -1
  289. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts +0 -10
  290. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.d.ts.map +0 -1
  291. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js +0 -16
  292. package/dist/analyzers/__tests__/fixtures/spec-17/import-node-builtin.js.map +0 -1
  293. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts +0 -14
  294. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.d.ts.map +0 -1
  295. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js +0 -18
  296. package/dist/analyzers/__tests__/fixtures/spec-17/jsx-event-handler.js.map +0 -1
  297. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts +0 -13
  298. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.d.ts.map +0 -1
  299. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js +0 -22
  300. package/dist/analyzers/__tests__/fixtures/spec-17/nested-loops-query.js.map +0 -1
  301. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts +0 -10
  302. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.d.ts.map +0 -1
  303. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js +0 -20
  304. package/dist/analyzers/__tests__/fixtures/spec-17/nine-line-repeated.js.map +0 -1
  305. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts +0 -18
  306. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.d.ts.map +0 -1
  307. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js +0 -28
  308. package/dist/analyzers/__tests__/fixtures/spec-17/private-methods.js.map +0 -1
  309. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts +0 -11
  310. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.d.ts.map +0 -1
  311. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js +0 -31
  312. package/dist/analyzers/__tests__/fixtures/spec-17/scope-all-config.js.map +0 -1
  313. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts +0 -11
  314. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.d.ts.map +0 -1
  315. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js +0 -18
  316. package/dist/analyzers/__tests__/fixtures/spec-17/sql-tagged-template.js.map +0 -1
  317. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts +0 -13
  318. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.d.ts.map +0 -1
  319. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js +0 -61
  320. package/dist/analyzers/__tests__/fixtures/spec-17/structural-similar-methods.js.map +0 -1
  321. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts +0 -10
  322. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.d.ts.map +0 -1
  323. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js +0 -15
  324. package/dist/analyzers/__tests__/fixtures/spec-17/template-prefix-table.js.map +0 -1
  325. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts +0 -11
  326. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.d.ts.map +0 -1
  327. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js +0 -49
  328. package/dist/analyzers/__tests__/fixtures/spec-17/token-identical-20-lines.js.map +0 -1
  329. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts +0 -10
  330. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.d.ts.map +0 -1
  331. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js +0 -19
  332. package/dist/analyzers/__tests__/fixtures/spec-17/tsx-no-db-usage.js.map +0 -1
  333. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts +0 -37
  334. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.d.ts.map +0 -1
  335. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js +0 -73
  336. package/dist/analyzers/__tests__/fixtures/spec-17/twenty-methods-vs-complex.js.map +0 -1
  337. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts +0 -12
  338. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.d.ts.map +0 -1
  339. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js +0 -82
  340. package/dist/analyzers/__tests__/fixtures/spec-17/unique-method-74-lines.js.map +0 -1
  341. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts +0 -10
  342. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.d.ts.map +0 -1
  343. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js +0 -15
  344. package/dist/analyzers/__tests__/fixtures/spec-17/word-the-in-comment.js.map +0 -1
  345. package/dist/analyzers/dataAccessAnalyzer.d.ts +0 -46
  346. package/dist/analyzers/dataAccessAnalyzer.d.ts.map +0 -1
  347. package/dist/analyzers/dataAccessAnalyzer.js +0 -474
  348. package/dist/analyzers/dataAccessAnalyzer.js.map +0 -1
  349. package/dist/analyzers/dryAnalyzer.d.ts +0 -31
  350. package/dist/analyzers/dryAnalyzer.d.ts.map +0 -1
  351. package/dist/analyzers/dryAnalyzer.js +0 -734
  352. package/dist/analyzers/dryAnalyzer.js.map +0 -1
  353. package/dist/analyzers/schemaAnalyzer.d.ts +0 -23
  354. package/dist/analyzers/schemaAnalyzer.d.ts.map +0 -1
  355. package/dist/analyzers/schemaAnalyzer.js +0 -564
  356. package/dist/analyzers/schemaAnalyzer.js.map +0 -1
  357. package/dist/analyzers/solidAnalyzer.d.ts +0 -33
  358. package/dist/analyzers/solidAnalyzer.d.ts.map +0 -1
  359. package/dist/analyzers/solidAnalyzer.js +0 -847
  360. package/dist/analyzers/solidAnalyzer.js.map +0 -1
@@ -1,847 +0,0 @@
1
- /**
2
- * SOLID Principles Analyzer - Functional Implementation
3
- * Detects violations of SOLID principles in TypeScript/JavaScript code
4
- */
5
- import { ResponsibilityType } from '../types.js';
6
- import { createViolation, getNodePosition, findNodesOfType, getNodeName, traverseAST, processFiles } from './analyzerUtils.js';
7
- import { hasModifier } from '../languages/adapterBridge.js';
8
- import { isReactComponent, getComponentName, detectComponentType, extractHooks, extractPropTypes } from '../utils/reactDetection.js';
9
- import { detectComponentResponsibilities, areResponsibilitiesRelated } from '../utils/componentResponsibility.js';
10
- import { detectComponentPattern, getUnrelatedResponsibilities } from '../utils/componentPatterns.js';
11
- import { CodeIndexDB } from '../codeIndexDB.js';
12
- import { WhitelistType } from '../types/whitelist.js';
13
- // ---------------------------------------------------------------------------
14
- // Helpers
15
- // ---------------------------------------------------------------------------
16
- /** Get raw text from a tree-sitter node (stored on ASTNode.raw). */
17
- function rawText(node) {
18
- if (!node)
19
- return '';
20
- return node.raw?.text ?? '';
21
- }
22
- /**
23
- * Default configuration
24
- */
25
- // Database instance for whitelist checking
26
- let dbInstance = null;
27
- async function getDB() {
28
- if (!dbInstance) {
29
- dbInstance = CodeIndexDB.getInstance();
30
- await dbInstance.initialize();
31
- }
32
- return dbInstance;
33
- }
34
- const DEFAULT_CONFIG = {
35
- maxMethodsPerClass: 15, // Increased from 10 - reasonable for service classes
36
- maxLinesPerMethod: 50,
37
- maxParametersPerMethod: 4,
38
- maxClassComplexity: 50,
39
- maxInterfaceMembers: 20, // Increased from 15 - configuration interfaces need more flexibility
40
- checkDependencyInversion: false, // Disabled by default - DI is not standard pattern
41
- checkInterfaceSegregation: true,
42
- checkLiskovSubstitution: true,
43
- // Component-specific defaults
44
- enableComponentSRP: true,
45
- componentPatterns: {
46
- patterns: [],
47
- customPatterns: undefined,
48
- enablePatternDetection: true
49
- },
50
- maxUnrelatedResponsibilities: 3, // Increased from 2 - allow URL management + UI + state
51
- contextAwareThresholds: true,
52
- checkUnrelatedResponsibilities: true // Check for unrelated responsibilities by default
53
- };
54
- /**
55
- * Helper: Check if import is from a framework/library
56
- */
57
- function isFrameworkImport(importPath) {
58
- const frameworkPatterns = [
59
- // React ecosystem
60
- /^(react|react-dom|react-router|react-redux)/,
61
- // Next.js
62
- /^(next|@next)\//,
63
- // Vue ecosystem
64
- /^(vue|vue-router|vuex|@vue)/,
65
- // Angular
66
- /^(@angular|angular)/,
67
- // UI frameworks
68
- /^@(mui|mantine|chakra-ui|ant-design|arco-design)/,
69
- // Node frameworks
70
- /^(express|fastify|koa|hapi|@nestjs)/,
71
- // Auth libraries
72
- /^(@clerk|@auth0|@stackframe|next-auth)/,
73
- // Database clients
74
- /^(pg|mongodb|@prisma|redis|ioredis)/,
75
- // HTTP clients
76
- /^(axios|node-fetch|got|ky)/,
77
- // Testing
78
- /^(@testing-library|jest|vitest|mocha|chai)/,
79
- // Build tools
80
- /^(webpack|vite|rollup|parcel|esbuild)/,
81
- // Common utilities that shouldn't trigger DI violations
82
- /^(lodash|underscore|ramda|date-fns|moment|dayjs)/,
83
- // Node modules indicator
84
- /node_modules/,
85
- // Anything from npm scope
86
- /^@[\w-]+\//
87
- ];
88
- return frameworkPatterns.some(pattern => pattern.test(importPath));
89
- }
90
- /**
91
- * Helper: Find import declaration for an identifier
92
- */
93
- function findImportForIdentifier(rootNode, identifier) {
94
- let importPath;
95
- traverseAST(rootNode, node => {
96
- if (node.type !== 'import_statement')
97
- return;
98
- const stringNode = node.children?.find(c => c.type === 'string');
99
- if (!stringNode)
100
- return;
101
- const moduleSpecifier = rawText(stringNode).replace(/^["']|["']$/g, '');
102
- const importClause = node.children?.find(c => c.type === 'import_clause');
103
- if (!importClause)
104
- return;
105
- // Check named imports
106
- const namedImports = importClause.children?.find(c => c.type === 'named_imports');
107
- if (namedImports) {
108
- for (const spec of namedImports.children ?? []) {
109
- if (spec.type !== 'import_specifier')
110
- continue;
111
- const identifiers = spec.children?.filter(c => c.type === 'identifier') ?? [];
112
- for (const idNode of identifiers) {
113
- if (rawText(idNode) === identifier) {
114
- importPath = moduleSpecifier;
115
- return;
116
- }
117
- }
118
- }
119
- }
120
- // Check default import
121
- const defaultImport = importClause.children?.find(c => c.type === 'identifier');
122
- if (defaultImport && rawText(defaultImport) === identifier) {
123
- importPath = moduleSpecifier;
124
- }
125
- });
126
- return importPath;
127
- }
128
- /**
129
- * Architectural layer detection for context-aware analysis
130
- */
131
- var ArchitecturalLayer;
132
- (function (ArchitecturalLayer) {
133
- ArchitecturalLayer["Route"] = "route";
134
- ArchitecturalLayer["Middleware"] = "middleware";
135
- ArchitecturalLayer["Component"] = "component";
136
- ArchitecturalLayer["Library"] = "library";
137
- ArchitecturalLayer["Repository"] = "repository";
138
- ArchitecturalLayer["Service"] = "service";
139
- ArchitecturalLayer["Hook"] = "hook";
140
- ArchitecturalLayer["Utility"] = "utility";
141
- ArchitecturalLayer["Config"] = "config";
142
- })(ArchitecturalLayer || (ArchitecturalLayer = {}));
143
- /**
144
- * Helper: Detect architectural layer from file path
145
- */
146
- function detectArchitecturalLayer(filePath) {
147
- const patterns = [
148
- // Next.js App Router patterns
149
- [ArchitecturalLayer.Route, /\/(app|pages)\/.*\/(route|page|layout|error|loading|not-found)\.(ts|tsx|js|jsx)$/],
150
- [ArchitecturalLayer.Route, /\/api\/.*\/route\.(ts|js)$/],
151
- // Middleware patterns
152
- [ArchitecturalLayer.Middleware, /middleware\.(ts|js)$/],
153
- [ArchitecturalLayer.Middleware, /\/(middleware|middlewares)\/.*\.(ts|js)$/],
154
- // Component patterns
155
- [ArchitecturalLayer.Component, /\/(components?|ui)\/.*\.(tsx|jsx)$/],
156
- // Repository patterns
157
- [ArchitecturalLayer.Repository, /\/(repositories|repository|repos?|dal|data-access)\/.*\.(ts|js)$/],
158
- // Service patterns
159
- [ArchitecturalLayer.Service, /\/(services?|use-cases|domain|business)\/.*\.(ts|js)$/],
160
- // Hook patterns
161
- [ArchitecturalLayer.Hook, /\/(hooks?)\/.*\.(ts|tsx|js|jsx)$/],
162
- [ArchitecturalLayer.Hook, /use[A-Z]\w+\.(ts|tsx|js|jsx)$/],
163
- // Utility patterns
164
- [ArchitecturalLayer.Utility, /\/(utils?|utilities|helpers?|lib)\/.*\.(ts|js)$/],
165
- // Config patterns
166
- [ArchitecturalLayer.Config, /\/(config|constants)\/.*\.(ts|js)$/],
167
- [ArchitecturalLayer.Config, /\.(config|constants)\.(ts|js)$/]
168
- ];
169
- for (const [layer, pattern] of patterns) {
170
- if (pattern.test(filePath)) {
171
- return layer;
172
- }
173
- }
174
- // Default to library for unmatched files
175
- return ArchitecturalLayer.Library;
176
- }
177
- /**
178
- * Helper: Get context-aware thresholds based on architectural layer
179
- */
180
- function getLayerThresholds(layer, config) {
181
- const layerConfigs = {
182
- [ArchitecturalLayer.Route]: {
183
- maxMethodsPerClass: 20, // Routes can have more methods
184
- maxUnrelatedResponsibilities: 4 // Routes often coordinate multiple concerns
185
- },
186
- [ArchitecturalLayer.Component]: {
187
- maxUnrelatedResponsibilities: 3, // Components can manage UI + state + events
188
- maxMethodsPerClass: 12
189
- },
190
- [ArchitecturalLayer.Repository]: {
191
- maxMethodsPerClass: 25, // Repositories can have many data access methods
192
- maxUnrelatedResponsibilities: 1 // Should focus on data access only
193
- },
194
- [ArchitecturalLayer.Service]: {
195
- maxMethodsPerClass: 15,
196
- maxUnrelatedResponsibilities: 2
197
- },
198
- [ArchitecturalLayer.Hook]: {
199
- maxMethodsPerClass: 5,
200
- maxUnrelatedResponsibilities: 1 // Hooks should have single purpose
201
- },
202
- [ArchitecturalLayer.Middleware]: {
203
- maxMethodsPerClass: 8,
204
- maxUnrelatedResponsibilities: 1
205
- },
206
- [ArchitecturalLayer.Utility]: {
207
- maxMethodsPerClass: 20, // Utility modules can have many functions
208
- maxUnrelatedResponsibilities: 3 // Utilities may group related functions
209
- },
210
- [ArchitecturalLayer.Config]: {
211
- maxMethodsPerClass: 50, // Config files may export many settings
212
- maxInterfaceMembers: 30 // Config interfaces need flexibility
213
- },
214
- [ArchitecturalLayer.Library]: {
215
- // Use defaults from config
216
- }
217
- };
218
- return config.contextAwareThresholds ? (layerConfigs[layer] || {}) : {};
219
- }
220
- /**
221
- * Helper: Check if file is a test file
222
- */
223
- function isTestFile(filePath) {
224
- const testPatterns = [
225
- /\.test\.[jt]sx?$/,
226
- /\.spec\.[jt]sx?$/,
227
- /\.test-d\.ts$/,
228
- /__tests__\//,
229
- /test\//,
230
- /tests\//,
231
- /spec\//,
232
- /\.e2e\.[jt]s$/,
233
- ];
234
- return testPatterns.some(pattern => pattern.test(filePath));
235
- }
236
- /**
237
- * Analyze a file for SOLID violations
238
- */
239
- const analyzeFile = async (filePath, ast, config) => {
240
- const violations = [];
241
- const isTest = isTestFile(filePath);
242
- const rootNode = ast.root;
243
- // Detect architectural layer for context-aware analysis
244
- const layer = detectArchitecturalLayer(filePath);
245
- const layerThresholds = getLayerThresholds(layer, config);
246
- // Layer thresholds should not override explicit user configuration
247
- // Only apply layer defaults if the user hasn't configured them
248
- const effectiveConfig = { ...layerThresholds, ...config };
249
- // Check Single Responsibility Principle for classes
250
- const classes = findNodesOfType(rootNode, n => n.type === 'class_declaration');
251
- classes.forEach(cls => {
252
- violations.push(...checkSingleResponsibility(cls, filePath, effectiveConfig));
253
- });
254
- // Check Single Responsibility Principle for React components
255
- if (config.enableComponentSRP) {
256
- const processedComponents = new Set();
257
- traverseAST(rootNode, node => {
258
- if (isReactComponent(node)) {
259
- // Skip if this is an arrow function that's part of a variable declaration
260
- // that we've already processed
261
- if (node.type === 'arrow_function' && node.parent?.type === 'variable_declarator' &&
262
- processedComponents.has(node.parent)) {
263
- return;
264
- }
265
- // For variable declarations with arrow functions, process the declaration
266
- // not the arrow function itself
267
- if (node.type === 'variable_declarator') {
268
- const initializer = node.children?.find(c => c.type !== 'identifier' && c.type !== 'type_annotation');
269
- if (initializer?.type === 'arrow_function') {
270
- if (!processedComponents.has(node)) {
271
- processedComponents.add(node);
272
- violations.push(...checkComponentSRP(node, filePath, effectiveConfig));
273
- }
274
- }
275
- }
276
- else if (!processedComponents.has(node)) {
277
- processedComponents.add(node);
278
- violations.push(...checkComponentSRP(node, filePath, effectiveConfig));
279
- }
280
- }
281
- });
282
- }
283
- // Check Open/Closed Principle
284
- traverseAST(rootNode, node => {
285
- violations.push(...checkOpenClosed(node, filePath, effectiveConfig));
286
- });
287
- // Check Liskov Substitution Principle
288
- if (effectiveConfig.checkLiskovSubstitution) {
289
- violations.push(...checkLiskovSubstitution(rootNode, filePath, effectiveConfig));
290
- }
291
- // Check Interface Segregation Principle
292
- if (effectiveConfig.checkInterfaceSegregation) {
293
- const interfaces = findNodesOfType(rootNode, n => n.type === 'interface_declaration');
294
- interfaces.forEach(iface => {
295
- violations.push(...checkInterfaceSegregation(iface, filePath, effectiveConfig));
296
- });
297
- }
298
- // Check Dependency Inversion Principle (skip for test files)
299
- if (effectiveConfig.checkDependencyInversion && !isTest) {
300
- violations.push(...await checkDependencyInversion(rootNode, filePath, effectiveConfig));
301
- }
302
- return violations;
303
- };
304
- /**
305
- * Check Single Responsibility Principle
306
- */
307
- function checkSingleResponsibility(node, filePath, config) {
308
- const violations = [];
309
- const className = getNodeName(node) || 'Anonymous';
310
- // Count methods
311
- const methods = findNodesOfType(node, n => n.type === 'method_definition');
312
- const publicMethods = methods.filter(m => !hasModifier(m, 'private'));
313
- if (publicMethods.length > config.maxMethodsPerClass) {
314
- const { line, column } = getNodePosition(node);
315
- violations.push(createViolation({
316
- analyzer: 'solid',
317
- file: filePath,
318
- line,
319
- column,
320
- severity: 'warning',
321
- message: `Class "${className}" has ${publicMethods.length} public methods (max: ${config.maxMethodsPerClass})`,
322
- type: 'solid',
323
- principle: 'single-responsibility',
324
- recommendation: 'Consider splitting this class into smaller, more focused classes',
325
- details: {
326
- className,
327
- methodCount: publicMethods.length,
328
- threshold: config.maxMethodsPerClass
329
- }
330
- }));
331
- }
332
- // Check for multiple responsibilities
333
- const responsibilities = detectResponsibilities(node);
334
- if (responsibilities.length > 1) {
335
- const { line, column } = getNodePosition(node);
336
- violations.push(createViolation({
337
- analyzer: 'solid',
338
- file: filePath,
339
- line,
340
- column,
341
- severity: 'warning',
342
- message: `Class "${className}" appears to have multiple responsibilities`,
343
- type: 'solid',
344
- principle: 'single-responsibility',
345
- recommendation: `Consider splitting based on: ${responsibilities.join(', ')}`,
346
- details: {
347
- className,
348
- responsibilities
349
- }
350
- }));
351
- }
352
- return violations;
353
- }
354
- /**
355
- * Check Open/Closed Principle
356
- */
357
- function checkOpenClosed(node, filePath, config) {
358
- const violations = [];
359
- // Check for long if-else chains
360
- if (node.type === 'if_statement') {
361
- const chainLength = countIfElseChain(node);
362
- if (chainLength > 3) {
363
- const { line, column } = getNodePosition(node);
364
- violations.push(createViolation({
365
- analyzer: 'solid',
366
- file: filePath,
367
- line,
368
- column,
369
- severity: 'warning',
370
- message: `Long if-else chain (${chainLength} branches) violates Open/Closed Principle`,
371
- type: 'solid',
372
- principle: 'open-closed',
373
- recommendation: 'Consider using polymorphism, strategy pattern, or a mapping object',
374
- details: {
375
- chainLength
376
- }
377
- }));
378
- }
379
- }
380
- // Check for switch statements with many cases
381
- if (node.type === 'switch_statement') {
382
- const cases = findNodesOfType(node, n => n.type === 'switch_case' || n.type === 'case_clause');
383
- if (cases.length > 5) {
384
- const { line, column } = getNodePosition(node);
385
- violations.push(createViolation({
386
- analyzer: 'solid',
387
- file: filePath,
388
- line,
389
- column,
390
- severity: 'warning',
391
- message: `Switch statement with ${cases.length} cases violates Open/Closed Principle`,
392
- type: 'solid',
393
- principle: 'open-closed',
394
- recommendation: 'Consider using polymorphism or a strategy pattern',
395
- details: {
396
- caseCount: cases.length
397
- }
398
- }));
399
- }
400
- }
401
- return violations;
402
- }
403
- /**
404
- * Check Liskov Substitution Principle
405
- */
406
- function checkLiskovSubstitution(rootNode, filePath, config) {
407
- const violations = [];
408
- // Find method overrides that throw errors
409
- traverseAST(rootNode, node => {
410
- if (node.type === 'method_definition' && hasModifier(node, 'override')) {
411
- const hasThrow = findNodesOfType(node, n => n.type === 'throw_statement').length > 0;
412
- const body = node.children?.find(c => c.type === 'statement_block');
413
- const bodyChildren = body?.children ?? [];
414
- const isNotImplemented = bodyChildren.length === 0 ||
415
- (bodyChildren.length === 1 && bodyChildren[0].type === 'throw_statement');
416
- if (hasThrow || isNotImplemented) {
417
- const { line, column } = getNodePosition(node);
418
- const methodName = getNodeName(node) || 'unknown';
419
- violations.push(createViolation({
420
- analyzer: 'solid',
421
- file: filePath,
422
- line,
423
- column,
424
- severity: 'critical',
425
- message: 'Method override throws error or is not implemented',
426
- type: 'solid',
427
- principle: 'liskov-substitution',
428
- recommendation: 'All overridden methods should maintain behavioral compatibility with base class',
429
- details: {
430
- methodName
431
- }
432
- }));
433
- }
434
- }
435
- });
436
- return violations;
437
- }
438
- /**
439
- * Check Interface Segregation Principle
440
- */
441
- function checkInterfaceSegregation(node, filePath, config) {
442
- const violations = [];
443
- const interfaceName = getNodeName(node) || 'Anonymous';
444
- // Count interface members (property_signature + method_signature children)
445
- const memberCount = (node.children ?? []).filter(c => c.type === 'property_signature' || c.type === 'method_signature').length;
446
- if (memberCount > config.maxInterfaceMembers) {
447
- const { line, column } = getNodePosition(node);
448
- violations.push(createViolation({
449
- analyzer: 'solid',
450
- file: filePath,
451
- line,
452
- column,
453
- severity: 'critical',
454
- message: `Interface "${interfaceName}" has too many members (${memberCount})`,
455
- type: 'solid',
456
- principle: 'interface-segregation',
457
- recommendation: 'Split this interface into smaller, more focused interfaces',
458
- details: {
459
- interfaceName,
460
- memberCount,
461
- threshold: config.maxInterfaceMembers
462
- }
463
- }));
464
- }
465
- return violations;
466
- }
467
- /**
468
- * Check Dependency Inversion Principle
469
- */
470
- async function checkDependencyInversion(rootNode, filePath, config) {
471
- const violations = [];
472
- // Skip import checking - DI principle should focus on instantiation, not imports
473
- // Imports are necessary and checking them creates too many false positives
474
- // Check for 'new' expressions (direct instantiation)
475
- const newExpressions = findNodesOfType(rootNode, n => n.type === 'new_expression');
476
- for (const expr of newExpressions) {
477
- // Find the class name identifier (first non-keyword named child)
478
- const exprIdent = expr.children?.find(c => c.type === 'identifier');
479
- if (!exprIdent)
480
- continue;
481
- const className = rawText(exprIdent);
482
- // Skip if this is inside a singleton pattern
483
- if (isInsideSingletonPattern(expr)) {
484
- continue;
485
- }
486
- // Skip if this is inside a factory method
487
- if (isInsideFactoryMethod(expr)) {
488
- continue;
489
- }
490
- // Skip Error classes - they need to be instantiated
491
- if (isErrorClass(className)) {
492
- continue;
493
- }
494
- // Check if this class is imported from a framework/library
495
- const importPath = findImportForIdentifier(rootNode, className);
496
- if (importPath && isFrameworkImport(importPath)) {
497
- continue; // Skip framework classes
498
- }
499
- if (await isConcreteClass(className)) {
500
- const { line, column } = getNodePosition(expr);
501
- violations.push(createViolation({
502
- analyzer: 'solid',
503
- file: filePath,
504
- line,
505
- column,
506
- severity: 'warning',
507
- message: `Direct instantiation of concrete class "${className}"`,
508
- type: 'solid',
509
- principle: 'dependency-inversion',
510
- recommendation: 'Consider using dependency injection or factory pattern',
511
- details: {
512
- className,
513
- importPath: importPath || 'local'
514
- }
515
- }));
516
- }
517
- }
518
- return violations;
519
- }
520
- /**
521
- * Helper: Detect responsibilities in a class
522
- */
523
- function detectResponsibilities(node) {
524
- const responsibilities = new Set();
525
- const methods = findNodesOfType(node, n => n.type === 'method_definition');
526
- methods.forEach(method => {
527
- const name = getNodeName(method)?.toLowerCase() || '';
528
- // Detect different concerns
529
- if (name.includes('save') || name.includes('load') || name.includes('persist')) {
530
- responsibilities.add('data persistence');
531
- }
532
- if (name.includes('validate') || name.includes('check')) {
533
- responsibilities.add('validation');
534
- }
535
- if (name.includes('render') || name.includes('display') || name.includes('show')) {
536
- responsibilities.add('presentation');
537
- }
538
- if (name.includes('send') || name.includes('fetch') || name.includes('request')) {
539
- responsibilities.add('communication');
540
- }
541
- if (name.includes('calculate') || name.includes('compute') || name.includes('process')) {
542
- responsibilities.add('business logic');
543
- }
544
- if (name.includes('log') || name.includes('track') || name.includes('monitor')) {
545
- responsibilities.add('logging/monitoring');
546
- }
547
- });
548
- // Check for too many public methods as a responsibility
549
- const publicMethods = methods.filter(m => !hasModifier(m, 'private'));
550
- if (publicMethods.length > 10) {
551
- responsibilities.add('too many methods');
552
- }
553
- return Array.from(responsibilities);
554
- }
555
- /**
556
- * Helper: Count if-else chain length
557
- */
558
- function countIfElseChain(node) {
559
- let count = 1;
560
- let elseClause = node.children?.find(c => c.type === 'else_clause');
561
- while (elseClause) {
562
- count++;
563
- const statement = elseClause.children?.find(c => c.type !== 'else');
564
- if (statement?.type === 'if_statement') {
565
- elseClause = statement.children?.find(c => c.type === 'else_clause');
566
- }
567
- else {
568
- break;
569
- }
570
- }
571
- return count;
572
- }
573
- /**
574
- * Helper: Check if import is a concrete dependency
575
- */
576
- async function isConcreteDependency(importPath) {
577
- try {
578
- const db = await getDB();
579
- // Skip relative paths (they're project internal) - don't flag these
580
- if (importPath.startsWith('.') || importPath.startsWith('/')) {
581
- return false; // Internal modules are fine
582
- }
583
- // Check if it's whitelisted directly
584
- if (db.isWhitelisted(importPath, WhitelistType.NodeBuiltin) ||
585
- db.isWhitelisted(importPath, WhitelistType.SharedLibrary) ||
586
- db.isWhitelisted(importPath, WhitelistType.ProjectDependency) ||
587
- db.isWhitelisted(importPath, WhitelistType.FrameworkClass)) {
588
- return false;
589
- }
590
- // For Node.js built-ins, also check the base module name
591
- // e.g., "fs/promises" -> check "fs", "node:fs/promises" -> check "fs"
592
- const baseModule = importPath.replace(/^node:/, '').split('/')[0];
593
- if (baseModule !== importPath) {
594
- if (db.isWhitelisted(baseModule, WhitelistType.NodeBuiltin)) {
595
- return false;
596
- }
597
- }
598
- // If not whitelisted, it's potentially a concrete dependency
599
- return true;
600
- }
601
- catch (error) {
602
- // If database is not available, be conservative and don't flag
603
- console.warn('Database not available for dependency check:', error);
604
- return false;
605
- }
606
- }
607
- /**
608
- * Helper: Check if class name is an Error class
609
- */
610
- function isErrorClass(className) {
611
- return className.endsWith('Error') ||
612
- className.endsWith('Exception') ||
613
- className === 'Error';
614
- }
615
- /**
616
- * Helper: Check if node is inside a singleton pattern
617
- */
618
- function isInsideSingletonPattern(node) {
619
- let parent = node.parent;
620
- while (parent) {
621
- // Check if we're inside a method named getInstance, instance, singleton, etc.
622
- if (parent.type === 'method_definition' || parent.type === 'function_declaration') {
623
- const methodName = getNodeName(parent)?.toLowerCase();
624
- if (methodName && (methodName.includes('getinstance') ||
625
- methodName === 'instance' ||
626
- methodName === 'singleton' ||
627
- (methodName === 'create' && parent.parent?.type === 'class_declaration'))) {
628
- return true;
629
- }
630
- }
631
- // Check if we're inside a static property initialization
632
- if (parent.type === 'public_field_definition' && hasModifier(parent, 'static')) {
633
- const propName = getNodeName(parent)?.toLowerCase();
634
- if (propName && (propName === 'instance' || propName === '_instance')) {
635
- return true;
636
- }
637
- }
638
- parent = parent.parent;
639
- }
640
- return false;
641
- }
642
- /**
643
- * Helper: Check if node is inside a factory method
644
- */
645
- function isInsideFactoryMethod(node) {
646
- let parent = node.parent;
647
- while (parent) {
648
- // Check if we're inside a method with 'create' or 'factory' in its name
649
- if (parent.type === 'method_definition' || parent.type === 'function_declaration') {
650
- const methodName = getNodeName(parent)?.toLowerCase();
651
- if (methodName && (methodName.includes('create') ||
652
- methodName.includes('factory') ||
653
- methodName.includes('make') ||
654
- methodName.includes('build'))) {
655
- return true;
656
- }
657
- }
658
- // Check if we're inside a class with Factory in its name
659
- if (parent.type === 'class_declaration') {
660
- const className = getNodeName(parent);
661
- if (className && className.toLowerCase().includes('factory')) {
662
- return true;
663
- }
664
- }
665
- parent = parent.parent;
666
- }
667
- return false;
668
- }
669
- /**
670
- * Helper: Check if class name represents concrete implementation
671
- */
672
- async function isConcreteClass(className) {
673
- try {
674
- const db = await getDB();
675
- // Check if it's whitelisted (platform API or framework class)
676
- if (db.isWhitelisted(className, WhitelistType.PlatformAPI) ||
677
- db.isWhitelisted(className, WhitelistType.FrameworkClass)) {
678
- return false;
679
- }
680
- // Skip if it looks like a type/interface name
681
- if (className.startsWith('I') && className.length > 2 && className[1] === className[1].toUpperCase()) {
682
- return false; // e.g., IUserService
683
- }
684
- if (className.endsWith('Interface') || className.endsWith('Type') || className.endsWith('Schema')) {
685
- return false;
686
- }
687
- return true;
688
- }
689
- catch (error) {
690
- // If database is not available, be conservative and don't flag
691
- console.warn('Database not available for class check:', error);
692
- return false;
693
- }
694
- }
695
- /**
696
- * Check Component Single Responsibility Principle
697
- */
698
- function checkComponentSRP(node, filePath, config) {
699
- const violations = [];
700
- // Extract component metadata
701
- const componentType = detectComponentType(node);
702
- if (!componentType)
703
- return violations;
704
- const componentName = getComponentName(node) || 'AnonymousComponent';
705
- const hooks = extractHooks(node);
706
- const props = extractPropTypes(node);
707
- const componentInfo = {
708
- name: componentName,
709
- filePath,
710
- lineNumber: getNodePosition(node).line,
711
- language: 'typescript',
712
- dependencies: [],
713
- purpose: '',
714
- context: '',
715
- entityType: 'component',
716
- componentType,
717
- hooks,
718
- props,
719
- isExported: true
720
- };
721
- // Detect component responsibilities
722
- const responsibilities = detectComponentResponsibilities(node, componentInfo);
723
- // Detect component pattern
724
- const pattern = detectComponentPattern(componentName, filePath, componentInfo.props?.map(p => p.name) || [], componentInfo.hooks?.map(h => h.name) || [], [] // TODO: Extract imports if needed
725
- );
726
- // Apply context-aware thresholds
727
- let effectiveMaxResponsibilities = config.maxUnrelatedResponsibilities;
728
- if (config.contextAwareThresholds && pattern) {
729
- // Check for pattern-specific override first
730
- if (config.patternThresholds?.[pattern.name]?.maxResponsibilities) {
731
- effectiveMaxResponsibilities = config.patternThresholds[pattern.name].maxResponsibilities;
732
- }
733
- else {
734
- // Fall back to multiplier-based calculation
735
- effectiveMaxResponsibilities = Math.ceil(config.maxUnrelatedResponsibilities * pattern.complexityMultiplier);
736
- }
737
- }
738
- // Get unrelated responsibilities
739
- const uniqueTypes = [...new Set(responsibilities.map(r => r.type))];
740
- const unrelatedGroups = [];
741
- if (pattern) {
742
- // Use pattern-specific analysis
743
- const patternUnrelated = getUnrelatedResponsibilities(uniqueTypes, pattern);
744
- unrelatedGroups.push(...patternUnrelated);
745
- }
746
- else {
747
- // Generic analysis - check for unrelated responsibility pairs
748
- for (let i = 0; i < uniqueTypes.length; i++) {
749
- for (let j = i + 1; j < uniqueTypes.length; j++) {
750
- if (!areResponsibilitiesRelated(uniqueTypes[i], uniqueTypes[j])) {
751
- unrelatedGroups.push([uniqueTypes[i], uniqueTypes[j]]);
752
- }
753
- }
754
- }
755
- }
756
- // Create violations only if there are actually unrelated responsibilities
757
- // Skip if there's only one responsibility or all responsibilities are related
758
- // Filter out single-element groups which aren't violations
759
- const realUnrelatedGroups = unrelatedGroups.filter(group => group.length >= 2);
760
- // Only check for unrelated groups if enabled in config
761
- if (config.checkUnrelatedResponsibilities && realUnrelatedGroups.length > 0 && uniqueTypes.length > 1) {
762
- const { line, column } = getNodePosition(node);
763
- // Main violation for mixed responsibilities
764
- violations.push(createViolation({
765
- analyzer: 'solid',
766
- file: filePath,
767
- line,
768
- column,
769
- severity: 'warning',
770
- message: `Component "${componentName}" has ${uniqueTypes.length} responsibilities with ${realUnrelatedGroups.length} unrelated groups`,
771
- type: 'solid',
772
- principle: 'single-responsibility',
773
- componentName,
774
- recommendation: generateComponentRefactoringRecommendation(responsibilities, pattern),
775
- details: {
776
- componentName,
777
- componentType: componentInfo.componentType,
778
- pattern: pattern?.name,
779
- responsibilities: uniqueTypes,
780
- unrelatedGroups: realUnrelatedGroups,
781
- hookCount: componentInfo.hooks?.length || 0,
782
- effectCount: responsibilities.filter(r => r.type === ResponsibilityType.SideEffects).length
783
- }
784
- }));
785
- }
786
- // Check for too many responsibilities overall
787
- if (uniqueTypes.length > effectiveMaxResponsibilities) {
788
- const { line, column } = getNodePosition(node);
789
- violations.push(createViolation({
790
- analyzer: 'solid',
791
- file: filePath,
792
- line,
793
- column,
794
- severity: 'critical',
795
- message: `Component "${componentName}" has too many responsibilities (${uniqueTypes.length} > ${effectiveMaxResponsibilities})`,
796
- type: 'solid',
797
- principle: 'single-responsibility',
798
- componentName,
799
- recommendation: 'Split this component into smaller, focused components',
800
- details: {
801
- componentName,
802
- responsibilityCount: uniqueTypes.length,
803
- threshold: effectiveMaxResponsibilities,
804
- detectedPattern: pattern?.name
805
- }
806
- }));
807
- }
808
- return violations;
809
- }
810
- /**
811
- * Generate component-specific refactoring recommendations
812
- */
813
- function generateComponentRefactoringRecommendation(responsibilities, pattern) {
814
- const types = [...new Set(responsibilities.map(r => r.type))];
815
- // Check for common anti-patterns
816
- if (types.includes(ResponsibilityType.DataFetching) && types.includes(ResponsibilityType.UIState)) {
817
- return 'Consider using container/presenter pattern: extract data fetching into a container component';
818
- }
819
- if (types.includes(ResponsibilityType.BusinessLogic) && types.includes(ResponsibilityType.FormHandling)) {
820
- return 'Extract business logic into custom hooks or utility functions';
821
- }
822
- if (types.includes(ResponsibilityType.SideEffects) && types.includes(ResponsibilityType.UIState)) {
823
- return 'Move side effects (analytics, logging) to a higher-level component or custom hook';
824
- }
825
- if (pattern?.name === 'Form' && types.includes(ResponsibilityType.DataFetching)) {
826
- return 'Forms should not fetch data directly. Move data fetching to parent component';
827
- }
828
- if (pattern?.name === 'SimpleUI' && types.length > 2) {
829
- return 'Simple UI components should only handle presentation. Extract logic to parent or hooks';
830
- }
831
- // Generic recommendation
832
- return 'Consider splitting by responsibility: ' + types.join(', ');
833
- }
834
- /**
835
- * SOLID analyzer definition
836
- */
837
- export const solidAnalyzer = {
838
- name: 'solid',
839
- defaultConfig: DEFAULT_CONFIG,
840
- analyze: async (files, config, options, progressCallback) => {
841
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
842
- return processFiles(files, analyzeFile, 'solid', mergedConfig, progressCallback ?
843
- (current, total, file) => progressCallback({ current, total, analyzer: 'solid', file }) :
844
- undefined);
845
- }
846
- };
847
- //# sourceMappingURL=solidAnalyzer.js.map