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,734 +0,0 @@
1
- /**
2
- * DRY (Don't Repeat Yourself) Analyzer (Functional)
3
- * Detects code duplication across the entire codebase
4
- *
5
- * Uses a code index to efficiently find:
6
- * - Exact code duplicates
7
- * - Similar code patterns
8
- * - Repeated string literals
9
- * - Duplicate imports
10
- */
11
- import * as crypto from 'crypto';
12
- import * as fs from 'fs/promises';
13
- import { parseFile, walkAST, findNodes, getNodeText, getLineAndColumn, getNodeName, } from '../languages/adapterBridge.js';
14
- /**
15
- * Debug logger
16
- */
17
- class DebugLogger {
18
- logs = [];
19
- enabled;
20
- constructor(enabled = false) {
21
- this.enabled = enabled;
22
- }
23
- log(message, data) {
24
- if (!this.enabled)
25
- return;
26
- const timestamp = new Date().toISOString();
27
- const logEntry = `[${timestamp}] ${message}`;
28
- if (data !== undefined) {
29
- this.logs.push(`${logEntry}\n${JSON.stringify(data, null, 2)}`);
30
- }
31
- else {
32
- this.logs.push(logEntry);
33
- }
34
- }
35
- async writeToFile(filePath) {
36
- if (!this.enabled || this.logs.length === 0)
37
- return;
38
- const content = this.logs.join('\n\n');
39
- await fs.writeFile(filePath, content, 'utf-8');
40
- }
41
- getLogs() {
42
- return this.logs;
43
- }
44
- }
45
- /**
46
- * Default configuration
47
- */
48
- const DEFAULT_CONFIG = {
49
- minLineThreshold: 5,
50
- similarityThreshold: 0.85,
51
- excludePatterns: ['**/*.test.ts', '**/*.spec.ts'],
52
- checkImports: true,
53
- checkStrings: true,
54
- checkUnusedImports: true,
55
- ignoreComments: true,
56
- ignoreWhitespace: true,
57
- debug: false,
58
- debugLogPath: './dry-analyzer-debug.log'
59
- };
60
- /**
61
- * Build a code index from all files
62
- */
63
- async function buildCodeIndex(files, config, logger) {
64
- const index = {
65
- blocks: [],
66
- hashMap: new Map(),
67
- stringLiterals: new Map(),
68
- imports: new Map(),
69
- unusedImports: new Map()
70
- };
71
- logger.log(`Building code index for ${files.length} files`);
72
- for (const file of files) {
73
- // Skip excluded patterns
74
- if (config.excludePatterns?.some(pattern => file.includes(pattern.replace('**/', '').replace('*', '')))) {
75
- logger.log(`Skipping excluded file: ${file}`);
76
- continue;
77
- }
78
- try {
79
- const content = await fs.readFile(file, 'utf-8');
80
- const ast = parseFile(file, content);
81
- if (!ast) {
82
- logger.log(`Failed to parse file: ${file}`);
83
- continue;
84
- }
85
- logger.log(`Processing file: ${file}`);
86
- // Extract code blocks
87
- const blocks = extractCodeBlocks(ast.root, content, file, config, logger);
88
- logger.log(`Extracted ${blocks.length} code blocks from ${file}`);
89
- for (const block of blocks) {
90
- index.blocks.push(block);
91
- // Add to hash map for O(1) duplicate lookup
92
- if (!index.hashMap.has(block.hash)) {
93
- index.hashMap.set(block.hash, []);
94
- }
95
- index.hashMap.get(block.hash).push(block);
96
- }
97
- // Extract string literals if enabled
98
- if (config.checkStrings) {
99
- const stringCount = index.stringLiterals.size;
100
- extractStringLiteralsFromAST(ast.root, content, file, index.stringLiterals, logger);
101
- logger.log(`Found ${index.stringLiterals.size - stringCount} new string literals`);
102
- }
103
- // Extract imports if enabled
104
- if (config.checkImports) {
105
- const importCount = index.imports.size;
106
- extractImportsFromAST(ast.root, content, file, index.imports, logger);
107
- logger.log(`Found ${index.imports.size - importCount} new import patterns`);
108
- }
109
- // Extract unused imports if enabled
110
- if (config.checkUnusedImports) {
111
- const unusedImportCount = index.unusedImports.size;
112
- extractUnusedImportsFromAST(ast.root, content, file, index.unusedImports, logger);
113
- logger.log(`Found ${index.unusedImports.size - unusedImportCount} new unused imports`);
114
- }
115
- }
116
- catch (error) {
117
- logger.log(`Error processing ${file}: ${error}`);
118
- }
119
- }
120
- logger.log('Code index built', {
121
- totalBlocks: index.blocks.length,
122
- uniqueHashes: index.hashMap.size,
123
- stringLiterals: index.stringLiterals.size,
124
- importPatterns: index.imports.size,
125
- unusedImports: index.unusedImports.size
126
- });
127
- return index;
128
- }
129
- /**
130
- * Extract code blocks from an AST
131
- */
132
- function extractCodeBlocks(ast, sourceCode, filePath, config, logger) {
133
- const blocks = [];
134
- // Extract functions
135
- const functions = findNodes(ast, n => n.type === 'function_declaration');
136
- for (const func of functions) {
137
- const block = createCodeBlock(sourceCode, func, filePath, 'function', config);
138
- if (block && isBlockLargeEnough(block, config)) {
139
- blocks.push(block);
140
- }
141
- }
142
- // Extract arrow functions
143
- const arrowFunctions = findNodes(ast, n => n.type === 'arrow_function' || n.type === 'function_expression');
144
- for (const arrow of arrowFunctions) {
145
- const block = createCodeBlock(sourceCode, arrow, filePath, 'function', config);
146
- if (block && isBlockLargeEnough(block, config)) {
147
- blocks.push(block);
148
- }
149
- }
150
- // Extract methods
151
- const methods = findNodes(ast, n => n.type === 'method_definition');
152
- for (const method of methods) {
153
- const block = createCodeBlock(sourceCode, method, filePath, 'method', config);
154
- if (block && isBlockLargeEnough(block, config)) {
155
- blocks.push(block);
156
- }
157
- }
158
- // Extract class declarations
159
- const classes = findNodes(ast, n => n.type === 'class_declaration');
160
- for (const cls of classes) {
161
- const block = createCodeBlock(sourceCode, cls, filePath, 'class', config);
162
- if (block && isBlockLargeEnough(block, config)) {
163
- blocks.push(block);
164
- }
165
- }
166
- // Extract block statements (if/else, loops, etc.)
167
- const blockStatements = findNodes(ast, n => n.type === 'statement_block');
168
- for (const blockStmt of blockStatements) {
169
- // Only consider substantial blocks
170
- const block = createCodeBlock(sourceCode, blockStmt, filePath, 'block', config);
171
- if (block && isBlockLargeEnough(block, config)) {
172
- blocks.push(block);
173
- }
174
- }
175
- return blocks;
176
- }
177
- /**
178
- * Create a code block from a node
179
- */
180
- function createCodeBlock(sourceCode, node, filePath, type, config) {
181
- const text = getNodeText(node, sourceCode);
182
- const normalizedText = normalizeCode(text, config);
183
- if (!normalizedText.trim()) {
184
- return null;
185
- }
186
- const { line: startLine } = getLineAndColumn(node);
187
- const endLine = node.location.end.line + 1;
188
- // Get component/property name if available
189
- const name = getNodeName(node) ?? undefined;
190
- return {
191
- file: filePath,
192
- startLine,
193
- endLine,
194
- text,
195
- normalizedText,
196
- hash: hashCode(normalizedText),
197
- type,
198
- name
199
- };
200
- }
201
- /**
202
- * Normalize code for comparison
203
- */
204
- function normalizeCode(code, config) {
205
- let normalized = code;
206
- if (config.ignoreComments) {
207
- // Remove single-line comments
208
- normalized = normalized.replace(/\/\/.*$/gm, '');
209
- // Remove multi-line comments
210
- normalized = normalized.replace(/\/\*[\s\S]*?\*\//g, '');
211
- }
212
- if (config.ignoreWhitespace) {
213
- // Normalize whitespace
214
- normalized = normalized.replace(/\s+/g, ' ').trim();
215
- }
216
- // Remove variable names to detect similar patterns
217
- normalized = normalized.replace(/\b(?:const|let|var)\s+(\w+)/g, 'VAR $1');
218
- return normalized;
219
- }
220
- /**
221
- * Hash code for fast comparison
222
- */
223
- function hashCode(text) {
224
- return crypto.createHash('md5').update(text).digest('hex');
225
- }
226
- /**
227
- * Check if a block is large enough to be considered
228
- */
229
- function isBlockLargeEnough(block, config) {
230
- const lineCount = block.endLine - block.startLine + 1;
231
- return lineCount >= (config.minLineThreshold || 5);
232
- }
233
- /**
234
- * Get string value (strip quotes from string literal)
235
- */
236
- function getStringValue(node, sourceCode) {
237
- const text = getNodeText(node, sourceCode);
238
- if ((text.startsWith("'") && text.endsWith("'")) ||
239
- (text.startsWith('"') && text.endsWith('"'))) {
240
- return text.slice(1, -1);
241
- }
242
- if (text.startsWith('`') && text.endsWith('`')) {
243
- return text.slice(1, -1);
244
- }
245
- return text;
246
- }
247
- /**
248
- * Extract string literals from AST
249
- */
250
- function extractStringLiteralsFromAST(ast, sourceCode, filePath, stringMap, logger) {
251
- // Find string and template_string nodes
252
- const stringLiterals = findNodes(ast, n => n.type === 'string' || n.type === 'string_fragment' || n.type === 'template_string');
253
- for (const literal of stringLiterals) {
254
- // Only process actual string content (skip quote-only nodes that might be children of template_string)
255
- const text = getStringValue(literal, sourceCode);
256
- // Skip short strings and common ones
257
- if (text.length < 10 || isCommonString(text)) {
258
- continue;
259
- }
260
- const { line } = getLineAndColumn(literal);
261
- if (!stringMap.has(text)) {
262
- stringMap.set(text, []);
263
- }
264
- stringMap.get(text).push({ file: filePath, line });
265
- }
266
- }
267
- /**
268
- * Check if a string is too common to track
269
- */
270
- function isCommonString(str) {
271
- const common = [
272
- 'use strict',
273
- 'default',
274
- 'exports',
275
- 'undefined',
276
- 'null',
277
- 'true',
278
- 'false',
279
- '',
280
- ' ',
281
- '\n',
282
- '\t'
283
- ];
284
- return common.includes(str) || /^[\s\d]+$/.test(str);
285
- }
286
- /**
287
- * Extract all import names from an import statement node
288
- */
289
- function extractImportNames(node, sourceCode) {
290
- const names = [];
291
- walkAST(node, (child) => {
292
- if (child.type === 'import_specifier') {
293
- // named import: "import { foo } from 'bar'"
294
- for (const c of (child.children || [])) {
295
- if (c.type === 'identifier') {
296
- names.push(getNodeText(c, sourceCode));
297
- }
298
- }
299
- }
300
- if (child.type === 'namespace_import') {
301
- // "import * as foo from 'bar'"
302
- for (const c of (child.children || [])) {
303
- if (c.type === 'identifier') {
304
- names.push('* as ' + getNodeText(c, sourceCode));
305
- }
306
- }
307
- }
308
- });
309
- // Also check for default import: "import foo from 'bar'"
310
- for (const child of (node.children || [])) {
311
- if (child.type === 'import_clause') {
312
- for (const c of (child.children || [])) {
313
- if (c.type === 'identifier') {
314
- const name = getNodeText(c, sourceCode);
315
- if (!names.includes(name)) {
316
- names.push(name);
317
- }
318
- }
319
- }
320
- }
321
- }
322
- return names;
323
- }
324
- /**
325
- * Extract imports from AST
326
- */
327
- function extractImportsFromAST(ast, sourceCode, filePath, importMap, logger) {
328
- const importNodes = findNodes(ast, n => n.type === 'import_statement');
329
- for (const node of importNodes) {
330
- let moduleSpecifier = '';
331
- const importedNames = [];
332
- // Find module specifier string
333
- for (const child of (node.children || [])) {
334
- if (child.type === 'string') {
335
- moduleSpecifier = getNodeText(child, sourceCode).replace(/['"]/g, '');
336
- }
337
- }
338
- if (!moduleSpecifier)
339
- continue;
340
- // Extract imported names
341
- const names = extractImportNames(node, sourceCode);
342
- importedNames.push(...names);
343
- const { line } = getLineAndColumn(node);
344
- const key = `${moduleSpecifier}:${importedNames.sort().join(',')}`;
345
- if (!importMap.has(key)) {
346
- importMap.set(key, []);
347
- }
348
- importMap.get(key).push({
349
- file: filePath,
350
- line,
351
- modules: importedNames
352
- });
353
- }
354
- }
355
- /**
356
- * Extract detailed per-name import information
357
- */
358
- function getImportDetails(ast, sourceCode) {
359
- const details = [];
360
- const importNodes = findNodes(ast, n => n.type === 'import_statement');
361
- for (const node of importNodes) {
362
- let moduleSpecifier = '';
363
- const { line } = getLineAndColumn(node);
364
- // Find module specifier string
365
- for (const child of (node.children || [])) {
366
- if (child.type === 'string') {
367
- moduleSpecifier = getNodeText(child, sourceCode).replace(/['"]/g, '');
368
- }
369
- }
370
- // Check for side-effect import (no import clause, no names)
371
- let hasImportClause = false;
372
- for (const child of (node.children || [])) {
373
- if (child.type === 'import_clause') {
374
- hasImportClause = true;
375
- break;
376
- }
377
- }
378
- if (!hasImportClause) {
379
- details.push({
380
- importType: 'side-effect',
381
- localName: moduleSpecifier,
382
- modulePath: moduleSpecifier,
383
- line
384
- });
385
- continue;
386
- }
387
- // Extract per-name imports
388
- const names = extractImportNames(node, sourceCode);
389
- for (const name of names) {
390
- details.push({
391
- importType: 'static',
392
- localName: name,
393
- modulePath: moduleSpecifier,
394
- line
395
- });
396
- }
397
- // If no names extracted but has import clause, it might be a namespace or default import not captured
398
- if (names.length === 0 && moduleSpecifier) {
399
- details.push({
400
- importType: 'static',
401
- localName: moduleSpecifier,
402
- modulePath: moduleSpecifier,
403
- line
404
- });
405
- }
406
- }
407
- return details;
408
- }
409
- /**
410
- * Extract identifier usage map from AST
411
- * Returns a Map of identifier name -> locations where it's used
412
- */
413
- function extractIdentifierUsageFromAST(ast, sourceCode, targetNames) {
414
- const usageMap = new Map();
415
- // Find all identifiers in the AST
416
- const identifiers = findNodes(ast, n => n.type === 'identifier');
417
- for (const id of identifiers) {
418
- const name = getNodeText(id, sourceCode);
419
- if (targetNames.has(name)) {
420
- if (!usageMap.has(name)) {
421
- usageMap.set(name, []);
422
- }
423
- usageMap.get(name).push({ line: getLineAndColumn(id).line });
424
- }
425
- }
426
- return usageMap;
427
- }
428
- /**
429
- * Extract unused imports from an AST
430
- */
431
- function extractUnusedImportsFromAST(ast, sourceCode, filePath, unusedImportsMap, logger) {
432
- // Get detailed imports
433
- const detailedImports = getImportDetails(ast, sourceCode);
434
- const importNames = new Set(detailedImports
435
- .filter(imp => imp.importType !== 'side-effect')
436
- .map(imp => imp.localName));
437
- // Extract identifier usage across the entire file
438
- const usageMap = extractIdentifierUsageFromAST(ast, sourceCode, importNames);
439
- // Find unused imports
440
- for (const imp of detailedImports) {
441
- // Skip side-effect imports - they're never "unused"
442
- if (imp.importType === 'side-effect')
443
- continue;
444
- if (!usageMap.has(imp.localName)) {
445
- const key = `${filePath}:${imp.localName}`;
446
- if (!unusedImportsMap.has(key)) {
447
- unusedImportsMap.set(key, []);
448
- }
449
- unusedImportsMap.get(key).push({
450
- file: filePath,
451
- line: imp.line,
452
- importName: imp.localName,
453
- moduleSpecifier: imp.modulePath
454
- });
455
- }
456
- }
457
- }
458
- /**
459
- * Find duplicates in the code index
460
- */
461
- function findDuplicates(index, config, logger) {
462
- const violations = [];
463
- // Find exact duplicates
464
- logger.log(`Checking ${index.hashMap.size} unique hashes for duplicates`);
465
- for (const [hash, blocks] of index.hashMap) {
466
- if (blocks.length > 1) {
467
- logger.log(`Hash ${hash} has ${blocks.length} blocks`);
468
- // Group by actual code (not just hash) to avoid false positives
469
- const groups = groupByExactCode(blocks);
470
- for (const group of groups) {
471
- if (group.length > 1) {
472
- logger.log(`Found exact duplicate with ${group.length} instances`, {
473
- files: group.map(b => ({ file: b.file, line: b.startLine, type: b.type })),
474
- preview: group[0].text.substring(0, 100) + '...'
475
- });
476
- violations.push(createDuplicateViolation(group, 'exact-duplicate'));
477
- }
478
- }
479
- }
480
- }
481
- // Find similar code blocks
482
- const similarViolations = findSimilarBlocks(index.blocks, config, logger);
483
- violations.push(...similarViolations);
484
- // Find duplicate strings
485
- if (config.checkStrings) {
486
- for (const [str, locations] of index.stringLiterals) {
487
- if (locations.length > 2) { // More than 2 occurrences
488
- violations.push(createStringDuplicateViolation(str, locations));
489
- }
490
- }
491
- }
492
- // Find duplicate imports
493
- if (config.checkImports) {
494
- for (const [importKey, locations] of index.imports) {
495
- if (locations.length > 3) { // More than 3 files with same imports
496
- violations.push(createImportDuplicateViolation(importKey, locations));
497
- }
498
- }
499
- }
500
- // Find unused imports
501
- if (config.checkUnusedImports) {
502
- for (const [importKey, locations] of index.unusedImports) {
503
- if (locations.length > 0) {
504
- violations.push(createUnusedImportViolation(locations[0]));
505
- }
506
- }
507
- }
508
- return violations;
509
- }
510
- /**
511
- * Group blocks by exact code content
512
- */
513
- function groupByExactCode(blocks) {
514
- const groups = new Map();
515
- for (const block of blocks) {
516
- const key = block.normalizedText;
517
- if (!groups.has(key)) {
518
- groups.set(key, []);
519
- }
520
- groups.get(key).push(block);
521
- }
522
- return Array.from(groups.values());
523
- }
524
- /**
525
- * Find similar (but not exact) code blocks
526
- */
527
- function findSimilarBlocks(blocks, config, logger) {
528
- const violations = [];
529
- const threshold = config.similarityThreshold || 0.85;
530
- const processed = new Set();
531
- for (let i = 0; i < blocks.length; i++) {
532
- const block1 = blocks[i];
533
- const key1 = `${block1.file}:${block1.startLine}`;
534
- if (processed.has(key1))
535
- continue;
536
- const similar = [block1];
537
- for (let j = i + 1; j < blocks.length; j++) {
538
- const block2 = blocks[j];
539
- const key2 = `${block2.file}:${block2.startLine}`;
540
- if (processed.has(key2))
541
- continue;
542
- // Skip if same hash (already handled in exact duplicates)
543
- if (block1.hash === block2.hash)
544
- continue;
545
- const similarity = calculateSimilarity(block1.normalizedText, block2.normalizedText);
546
- if (similarity >= threshold) {
547
- similar.push(block2);
548
- processed.add(key2);
549
- }
550
- }
551
- if (similar.length > 1) {
552
- violations.push(createDuplicateViolation(similar, 'similar-logic', calculateAverageSimilarity(similar)));
553
- similar.forEach(b => processed.add(`${b.file}:${b.startLine}`));
554
- }
555
- }
556
- return violations;
557
- }
558
- /**
559
- * Calculate similarity between two strings (0-1)
560
- */
561
- function calculateSimilarity(str1, str2) {
562
- // Simple token-based similarity
563
- const tokens1 = tokenize(str1);
564
- const tokens2 = tokenize(str2);
565
- const set1 = new Set(tokens1);
566
- const set2 = new Set(tokens2);
567
- const intersection = new Set([...set1].filter(x => set2.has(x)));
568
- const union = new Set([...set1, ...set2]);
569
- // Jaccard similarity
570
- const jaccard = intersection.size / union.size;
571
- // Length similarity
572
- const lengthSim = Math.min(str1.length, str2.length) / Math.max(str1.length, str2.length);
573
- // Combined similarity
574
- return (jaccard * 0.7 + lengthSim * 0.3);
575
- }
576
- /**
577
- * Tokenize code for similarity comparison
578
- */
579
- function tokenize(code) {
580
- return code
581
- .split(/\s+/)
582
- .filter(token => token.length > 0)
583
- .map(token => token.toLowerCase());
584
- }
585
- /**
586
- * Calculate average similarity for a group
587
- */
588
- function calculateAverageSimilarity(blocks) {
589
- if (blocks.length < 2)
590
- return 1;
591
- let totalSim = 0;
592
- let count = 0;
593
- for (let i = 0; i < blocks.length - 1; i++) {
594
- for (let j = i + 1; j < blocks.length; j++) {
595
- totalSim += calculateSimilarity(blocks[i].normalizedText, blocks[j].normalizedText);
596
- count++;
597
- }
598
- }
599
- return totalSim / count;
600
- }
601
- /**
602
- * Create a duplicate violation
603
- */
604
- function createDuplicateViolation(blocks, type, similarity) {
605
- const primary = blocks[0];
606
- const locations = blocks.map(b => ({ file: b.file, line: b.startLine }));
607
- const totalLines = blocks.reduce((sum, b) => sum + (b.endLine - b.startLine + 1), 0);
608
- return {
609
- analyzer: 'dry',
610
- file: primary.file,
611
- line: primary.startLine,
612
- severity: type === 'exact-duplicate' ? 'warning' : 'suggestion',
613
- type,
614
- message: `${type === 'exact-duplicate' ? 'Exact duplicate' : 'Similar'} code found in ${blocks.length} locations`,
615
- recommendation: `Consider extracting this ${primary.type} into a shared utility function`,
616
- locations,
617
- similarity,
618
- metrics: {
619
- duplicateLines: totalLines - (primary.endLine - primary.startLine + 1),
620
- totalLines
621
- },
622
- estimatedEffort: 'medium'
623
- };
624
- }
625
- /**
626
- * Create a string duplicate violation
627
- */
628
- function createStringDuplicateViolation(str, locations) {
629
- return {
630
- analyzer: 'dry',
631
- file: locations[0].file,
632
- line: locations[0].line,
633
- severity: 'suggestion',
634
- type: 'exact-duplicate',
635
- message: `String literal "${str.substring(0, 50)}${str.length > 50 ? '...' : ''}" appears ${locations.length} times`,
636
- recommendation: 'Consider extracting this string into a named constant',
637
- locations,
638
- estimatedEffort: 'small'
639
- };
640
- }
641
- /**
642
- * Create an import duplicate violation
643
- */
644
- function createImportDuplicateViolation(importKey, locations) {
645
- const [moduleSpec] = importKey.split(':');
646
- return {
647
- analyzer: 'dry',
648
- file: locations[0].file,
649
- line: locations[0].line,
650
- severity: 'suggestion',
651
- type: 'pattern-duplication',
652
- message: `Same import pattern from "${moduleSpec}" used in ${locations.length} files`,
653
- recommendation: 'Consider creating a barrel export or shared import module',
654
- locations: locations.map(l => ({ file: l.file, line: l.line })),
655
- estimatedEffort: 'small'
656
- };
657
- }
658
- /**
659
- * Create an unused import violation
660
- */
661
- function createUnusedImportViolation(location) {
662
- return {
663
- analyzer: 'dry',
664
- file: location.file,
665
- line: location.line,
666
- severity: 'suggestion',
667
- type: 'pattern-duplication',
668
- message: `Unused import '${location.importName}' from '${location.moduleSpecifier}'`,
669
- recommendation: `Remove this unused import to keep the codebase clean and reduce bundle size`,
670
- estimatedEffort: 'small'
671
- };
672
- }
673
- /**
674
- * DRY Analyzer definition
675
- */
676
- export const dryAnalyzer = {
677
- name: 'dry',
678
- defaultConfig: DEFAULT_CONFIG,
679
- analyze: async (files, config, options, progressCallback) => {
680
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
681
- const logger = new DebugLogger(mergedConfig.debug || false);
682
- logger.log('DRY Analyzer started', {
683
- filesCount: files.length,
684
- config: mergedConfig
685
- });
686
- // Report initial progress
687
- if (progressCallback) {
688
- progressCallback({
689
- current: 0,
690
- total: files.length,
691
- analyzer: 'dry',
692
- phase: 'indexing'
693
- });
694
- }
695
- // Build code index
696
- const startTime = Date.now();
697
- const index = await buildCodeIndex(files, mergedConfig, logger);
698
- // Report analysis progress
699
- if (progressCallback) {
700
- progressCallback({
701
- current: files.length / 2,
702
- total: files.length,
703
- analyzer: 'dry',
704
- phase: 'analyzing'
705
- });
706
- }
707
- // Find duplicates
708
- const violations = findDuplicates(index, mergedConfig, logger);
709
- logger.log('DRY Analysis complete', {
710
- violationsFound: violations.length,
711
- executionTime: Date.now() - startTime
712
- });
713
- // Write debug log to file
714
- if (mergedConfig.debug && mergedConfig.debugLogPath) {
715
- await logger.writeToFile(mergedConfig.debugLogPath);
716
- }
717
- // Complete
718
- if (progressCallback) {
719
- progressCallback({
720
- current: files.length,
721
- total: files.length,
722
- analyzer: 'dry',
723
- phase: 'complete'
724
- });
725
- }
726
- return {
727
- violations,
728
- filesProcessed: files.length,
729
- executionTime: Date.now() - startTime,
730
- analyzerName: 'dry'
731
- };
732
- }
733
- };
734
- //# sourceMappingURL=dryAnalyzer.js.map