mcp-react-toolkit 1.0.0 → 1.2.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 (235) hide show
  1. package/README.md +146 -42
  2. package/package.json +13 -5
  3. package/server.json +20 -0
  4. package/tools/accessibility-checker/build/index.js +9 -5
  5. package/tools/accessibility-checker/build/index.js.map +1 -1
  6. package/tools/accessibility-checker/build/rules.d.ts.map +1 -1
  7. package/tools/accessibility-checker/build/rules.js +325 -94
  8. package/tools/accessibility-checker/build/rules.js.map +1 -1
  9. package/tools/code-modernizer/build/tools/01-convert-to-typescript.d.ts.map +1 -1
  10. package/tools/code-modernizer/build/tools/01-convert-to-typescript.js +65 -50
  11. package/tools/code-modernizer/build/tools/01-convert-to-typescript.js.map +1 -1
  12. package/tools/code-modernizer/build/types.d.ts +1 -0
  13. package/tools/code-modernizer/build/types.d.ts.map +1 -1
  14. package/tools/code-modernizer/build/utils/ast-parser.d.ts.map +1 -1
  15. package/tools/code-modernizer/build/utils/ast-parser.js +30 -14
  16. package/tools/code-modernizer/build/utils/ast-parser.js.map +1 -1
  17. package/tools/code-modernizer/build/utils/type-generator.d.ts +1 -1
  18. package/tools/code-modernizer/build/utils/type-generator.d.ts.map +1 -1
  19. package/tools/code-modernizer/build/utils/type-generator.js +72 -23
  20. package/tools/code-modernizer/build/utils/type-generator.js.map +1 -1
  21. package/tools/component-factory/build/index.js +59 -7
  22. package/tools/component-factory/build/index.js.map +1 -1
  23. package/tools/component-fixer/README.md +44 -0
  24. package/tools/component-fixer/build/index.d.ts +3 -0
  25. package/tools/component-fixer/build/index.d.ts.map +1 -0
  26. package/tools/component-fixer/build/index.js +647 -0
  27. package/tools/component-fixer/build/index.js.map +1 -0
  28. package/tools/component-fixer/build/index.test.d.ts +2 -0
  29. package/tools/component-fixer/build/index.test.d.ts.map +1 -0
  30. package/tools/component-fixer/build/index.test.js.map +1 -0
  31. package/tools/component-fixer/package.json +20 -0
  32. package/tools/component-reviewer/README.md +54 -0
  33. package/tools/component-reviewer/build/index.d.ts +39 -0
  34. package/tools/component-reviewer/build/index.d.ts.map +1 -0
  35. package/tools/component-reviewer/build/index.js +946 -0
  36. package/tools/component-reviewer/build/index.js.map +1 -0
  37. package/tools/component-reviewer/build/index.test.d.ts +2 -0
  38. package/tools/component-reviewer/build/index.test.d.ts.map +1 -0
  39. package/tools/component-reviewer/build/index.test.js.map +1 -0
  40. package/tools/component-reviewer/package.json +20 -0
  41. package/tools/dep-auditor/build/index.d.ts +1 -0
  42. package/tools/dep-auditor/build/index.d.ts.map +1 -1
  43. package/tools/dep-auditor/build/index.js +71 -16
  44. package/tools/dep-auditor/build/index.js.map +1 -1
  45. package/tools/generate-tests/build/analyzer.d.ts +14 -0
  46. package/tools/generate-tests/build/analyzer.d.ts.map +1 -1
  47. package/tools/generate-tests/build/analyzer.js +96 -42
  48. package/tools/generate-tests/build/analyzer.js.map +1 -1
  49. package/tools/generate-tests/build/generators.d.ts.map +1 -1
  50. package/tools/generate-tests/build/generators.js +304 -79
  51. package/tools/generate-tests/build/generators.js.map +1 -1
  52. package/tools/generate-tests/build/index.js +29 -10
  53. package/tools/generate-tests/build/index.js.map +1 -1
  54. package/tools/json-viewer/build/index.js +29 -6
  55. package/tools/json-viewer/build/index.js.map +1 -1
  56. package/tools/legacy-analyzer/README.md +66 -0
  57. package/tools/legacy-analyzer/build/index.d.ts +3 -0
  58. package/tools/legacy-analyzer/build/index.d.ts.map +1 -0
  59. package/tools/legacy-analyzer/build/index.js +209 -0
  60. package/tools/legacy-analyzer/build/index.js.map +1 -0
  61. package/tools/legacy-analyzer/build/index.test.d.ts +2 -0
  62. package/tools/legacy-analyzer/build/index.test.d.ts.map +1 -0
  63. package/tools/legacy-analyzer/build/index.test.js.map +1 -0
  64. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.d.ts +3 -0
  65. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.d.ts.map +1 -0
  66. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js +115 -0
  67. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js.map +1 -0
  68. package/tools/legacy-analyzer/build/tools/02-analyze-folder-structure.d.ts +3 -0
  69. package/tools/legacy-analyzer/build/tools/02-analyze-folder-structure.d.ts.map +1 -0
  70. package/tools/legacy-analyzer/build/tools/02-analyze-folder-structure.js +85 -0
  71. package/tools/legacy-analyzer/build/tools/02-analyze-folder-structure.js.map +1 -0
  72. package/tools/legacy-analyzer/build/tools/03-analyze-components.d.ts +3 -0
  73. package/tools/legacy-analyzer/build/tools/03-analyze-components.d.ts.map +1 -0
  74. package/tools/legacy-analyzer/build/tools/03-analyze-components.js +87 -0
  75. package/tools/legacy-analyzer/build/tools/03-analyze-components.js.map +1 -0
  76. package/tools/legacy-analyzer/build/tools/04-analyze-state-management.d.ts +3 -0
  77. package/tools/legacy-analyzer/build/tools/04-analyze-state-management.d.ts.map +1 -0
  78. package/tools/legacy-analyzer/build/tools/04-analyze-state-management.js +133 -0
  79. package/tools/legacy-analyzer/build/tools/04-analyze-state-management.js.map +1 -0
  80. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.d.ts +3 -0
  81. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.d.ts.map +1 -0
  82. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js +160 -0
  83. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js.map +1 -0
  84. package/tools/legacy-analyzer/build/tools/06-analyze-routing.d.ts +3 -0
  85. package/tools/legacy-analyzer/build/tools/06-analyze-routing.d.ts.map +1 -0
  86. package/tools/legacy-analyzer/build/tools/06-analyze-routing.js +150 -0
  87. package/tools/legacy-analyzer/build/tools/06-analyze-routing.js.map +1 -0
  88. package/tools/legacy-analyzer/build/tools/07-analyze-styling.d.ts +3 -0
  89. package/tools/legacy-analyzer/build/tools/07-analyze-styling.d.ts.map +1 -0
  90. package/tools/legacy-analyzer/build/tools/07-analyze-styling.js +131 -0
  91. package/tools/legacy-analyzer/build/tools/07-analyze-styling.js.map +1 -0
  92. package/tools/legacy-analyzer/build/tools/08-analyze-assets.d.ts +3 -0
  93. package/tools/legacy-analyzer/build/tools/08-analyze-assets.d.ts.map +1 -0
  94. package/tools/legacy-analyzer/build/tools/08-analyze-assets.js +85 -0
  95. package/tools/legacy-analyzer/build/tools/08-analyze-assets.js.map +1 -0
  96. package/tools/legacy-analyzer/build/tools/09-detect-anti-patterns.d.ts +3 -0
  97. package/tools/legacy-analyzer/build/tools/09-detect-anti-patterns.d.ts.map +1 -0
  98. package/tools/legacy-analyzer/build/tools/09-detect-anti-patterns.js +329 -0
  99. package/tools/legacy-analyzer/build/tools/09-detect-anti-patterns.js.map +1 -0
  100. package/tools/legacy-analyzer/build/tools/10-detect-duplication.d.ts +3 -0
  101. package/tools/legacy-analyzer/build/tools/10-detect-duplication.d.ts.map +1 -0
  102. package/tools/legacy-analyzer/build/tools/10-detect-duplication.js +192 -0
  103. package/tools/legacy-analyzer/build/tools/10-detect-duplication.js.map +1 -0
  104. package/tools/legacy-analyzer/build/tools/11-analyze-dependencies-usage.d.ts +3 -0
  105. package/tools/legacy-analyzer/build/tools/11-analyze-dependencies-usage.d.ts.map +1 -0
  106. package/tools/legacy-analyzer/build/tools/11-analyze-dependencies-usage.js +232 -0
  107. package/tools/legacy-analyzer/build/tools/11-analyze-dependencies-usage.js.map +1 -0
  108. package/tools/legacy-analyzer/build/tools/12-analyze-legacy-app.d.ts +3 -0
  109. package/tools/legacy-analyzer/build/tools/12-analyze-legacy-app.d.ts.map +1 -0
  110. package/tools/legacy-analyzer/build/tools/12-analyze-legacy-app.js +247 -0
  111. package/tools/legacy-analyzer/build/tools/12-analyze-legacy-app.js.map +1 -0
  112. package/tools/legacy-analyzer/build/tools/13-detect-features.d.ts +3 -0
  113. package/tools/legacy-analyzer/build/tools/13-detect-features.d.ts.map +1 -0
  114. package/tools/legacy-analyzer/build/tools/13-detect-features.js +141 -0
  115. package/tools/legacy-analyzer/build/tools/13-detect-features.js.map +1 -0
  116. package/tools/legacy-analyzer/build/tools/14-classify-files.d.ts +3 -0
  117. package/tools/legacy-analyzer/build/tools/14-classify-files.d.ts.map +1 -0
  118. package/tools/legacy-analyzer/build/tools/14-classify-files.js +76 -0
  119. package/tools/legacy-analyzer/build/tools/14-classify-files.js.map +1 -0
  120. package/tools/legacy-analyzer/build/tools/15-detect-shared-modules.d.ts +3 -0
  121. package/tools/legacy-analyzer/build/tools/15-detect-shared-modules.d.ts.map +1 -0
  122. package/tools/legacy-analyzer/build/tools/15-detect-shared-modules.js +70 -0
  123. package/tools/legacy-analyzer/build/tools/15-detect-shared-modules.js.map +1 -0
  124. package/tools/legacy-analyzer/build/tools/16-design-target-structure.d.ts +3 -0
  125. package/tools/legacy-analyzer/build/tools/16-design-target-structure.d.ts.map +1 -0
  126. package/tools/legacy-analyzer/build/tools/16-design-target-structure.js +26 -0
  127. package/tools/legacy-analyzer/build/tools/16-design-target-structure.js.map +1 -0
  128. package/tools/legacy-analyzer/build/tools/17-map-files-to-target.d.ts +3 -0
  129. package/tools/legacy-analyzer/build/tools/17-map-files-to-target.d.ts.map +1 -0
  130. package/tools/legacy-analyzer/build/tools/17-map-files-to-target.js +108 -0
  131. package/tools/legacy-analyzer/build/tools/17-map-files-to-target.js.map +1 -0
  132. package/tools/legacy-analyzer/build/tools/18-detect-boundary-violations.d.ts +3 -0
  133. package/tools/legacy-analyzer/build/tools/18-detect-boundary-violations.d.ts.map +1 -0
  134. package/tools/legacy-analyzer/build/tools/18-detect-boundary-violations.js +137 -0
  135. package/tools/legacy-analyzer/build/tools/18-detect-boundary-violations.js.map +1 -0
  136. package/tools/legacy-analyzer/build/tools/19-suggest-module-splitting.d.ts +3 -0
  137. package/tools/legacy-analyzer/build/tools/19-suggest-module-splitting.d.ts.map +1 -0
  138. package/tools/legacy-analyzer/build/tools/19-suggest-module-splitting.js +160 -0
  139. package/tools/legacy-analyzer/build/tools/19-suggest-module-splitting.js.map +1 -0
  140. package/tools/legacy-analyzer/build/tools/20-naming-standardizer.d.ts +3 -0
  141. package/tools/legacy-analyzer/build/tools/20-naming-standardizer.d.ts.map +1 -0
  142. package/tools/legacy-analyzer/build/tools/20-naming-standardizer.js +162 -0
  143. package/tools/legacy-analyzer/build/tools/20-naming-standardizer.js.map +1 -0
  144. package/tools/legacy-analyzer/build/tools/21-generate-refactor-plan.d.ts +3 -0
  145. package/tools/legacy-analyzer/build/tools/21-generate-refactor-plan.d.ts.map +1 -0
  146. package/tools/legacy-analyzer/build/tools/21-generate-refactor-plan.js +108 -0
  147. package/tools/legacy-analyzer/build/tools/21-generate-refactor-plan.js.map +1 -0
  148. package/tools/legacy-analyzer/build/tools/22-refactor-folder-structure.d.ts +3 -0
  149. package/tools/legacy-analyzer/build/tools/22-refactor-folder-structure.d.ts.map +1 -0
  150. package/tools/legacy-analyzer/build/tools/22-refactor-folder-structure.js +98 -0
  151. package/tools/legacy-analyzer/build/tools/22-refactor-folder-structure.js.map +1 -0
  152. package/tools/legacy-analyzer/build/types.d.ts +413 -0
  153. package/tools/legacy-analyzer/build/types.d.ts.map +1 -0
  154. package/tools/legacy-analyzer/build/types.js +12 -0
  155. package/tools/legacy-analyzer/build/types.js.map +1 -0
  156. package/tools/legacy-analyzer/build/utils/ast-parser.d.ts +34 -0
  157. package/tools/legacy-analyzer/build/utils/ast-parser.d.ts.map +1 -0
  158. package/tools/legacy-analyzer/build/utils/ast-parser.js +394 -0
  159. package/tools/legacy-analyzer/build/utils/ast-parser.js.map +1 -0
  160. package/tools/legacy-analyzer/build/utils/file-scanner.d.ts +51 -0
  161. package/tools/legacy-analyzer/build/utils/file-scanner.d.ts.map +1 -0
  162. package/tools/legacy-analyzer/build/utils/file-scanner.js +174 -0
  163. package/tools/legacy-analyzer/build/utils/file-scanner.js.map +1 -0
  164. package/tools/legacy-analyzer/build/utils/import-tracker.d.ts +38 -0
  165. package/tools/legacy-analyzer/build/utils/import-tracker.d.ts.map +1 -0
  166. package/tools/legacy-analyzer/build/utils/import-tracker.js +194 -0
  167. package/tools/legacy-analyzer/build/utils/import-tracker.js.map +1 -0
  168. package/tools/legacy-analyzer/build/utils/refactor-helpers.d.ts +88 -0
  169. package/tools/legacy-analyzer/build/utils/refactor-helpers.d.ts.map +1 -0
  170. package/tools/legacy-analyzer/build/utils/refactor-helpers.js +538 -0
  171. package/tools/legacy-analyzer/build/utils/refactor-helpers.js.map +1 -0
  172. package/tools/legacy-analyzer/package.json +20 -0
  173. package/tools/lighthouse-runner/README.md +45 -0
  174. package/tools/lighthouse-runner/build/index.d.ts +6 -0
  175. package/tools/lighthouse-runner/build/index.d.ts.map +1 -0
  176. package/tools/lighthouse-runner/build/index.js +295 -0
  177. package/tools/lighthouse-runner/build/index.js.map +1 -0
  178. package/tools/lighthouse-runner/build/index.test.d.ts +2 -0
  179. package/tools/lighthouse-runner/build/index.test.d.ts.map +1 -0
  180. package/tools/lighthouse-runner/build/index.test.js.map +1 -0
  181. package/tools/lighthouse-runner/package.json +20 -0
  182. package/tools/monorepo-manager/build/utils.d.ts.map +1 -1
  183. package/tools/monorepo-manager/build/utils.js +12 -9
  184. package/tools/monorepo-manager/build/utils.js.map +1 -1
  185. package/tools/performance-audit/README.md +37 -0
  186. package/tools/performance-audit/build/index.d.ts +13 -0
  187. package/tools/performance-audit/build/index.d.ts.map +1 -0
  188. package/tools/performance-audit/build/index.js +311 -0
  189. package/tools/performance-audit/build/index.js.map +1 -0
  190. package/tools/performance-audit/build/index.test.d.ts +2 -0
  191. package/tools/performance-audit/build/index.test.d.ts.map +1 -0
  192. package/tools/performance-audit/build/index.test.js.map +1 -0
  193. package/tools/performance-audit/package.json +20 -0
  194. package/tools/quality-pipeline/build/index.js +55 -15
  195. package/tools/quality-pipeline/build/index.js.map +1 -1
  196. package/tools/render-analyzer/README.md +43 -0
  197. package/tools/render-analyzer/build/index.d.ts +25 -0
  198. package/tools/render-analyzer/build/index.d.ts.map +1 -0
  199. package/tools/render-analyzer/build/index.js +342 -0
  200. package/tools/render-analyzer/build/index.js.map +1 -0
  201. package/tools/render-analyzer/build/index.test.d.ts +2 -0
  202. package/tools/render-analyzer/build/index.test.d.ts.map +1 -0
  203. package/tools/render-analyzer/build/index.test.js.map +1 -0
  204. package/tools/render-analyzer/package.json +20 -0
  205. package/tools/shared/build/fs.d.ts +8 -0
  206. package/tools/shared/build/fs.d.ts.map +1 -0
  207. package/tools/shared/build/fs.js +45 -0
  208. package/tools/shared/build/fs.js.map +1 -0
  209. package/tools/shared/build/index.d.ts +1 -0
  210. package/tools/shared/build/index.d.ts.map +1 -1
  211. package/tools/shared/build/index.js +1 -0
  212. package/tools/shared/build/index.js.map +1 -1
  213. package/tools/storybook-generator/README.md +39 -0
  214. package/tools/storybook-generator/build/index.d.ts +13 -0
  215. package/tools/storybook-generator/build/index.d.ts.map +1 -0
  216. package/tools/storybook-generator/build/index.js +478 -0
  217. package/tools/storybook-generator/build/index.js.map +1 -0
  218. package/tools/storybook-generator/build/index.test.d.ts +2 -0
  219. package/tools/storybook-generator/build/index.test.d.ts.map +1 -0
  220. package/tools/storybook-generator/build/index.test.js.map +1 -0
  221. package/tools/storybook-generator/package.json +20 -0
  222. package/tools/test-gap-analyzer/README.md +41 -0
  223. package/tools/test-gap-analyzer/build/index.d.ts +20 -0
  224. package/tools/test-gap-analyzer/build/index.d.ts.map +1 -0
  225. package/tools/test-gap-analyzer/build/index.js +371 -0
  226. package/tools/test-gap-analyzer/build/index.js.map +1 -0
  227. package/tools/test-gap-analyzer/build/index.test.d.ts +2 -0
  228. package/tools/test-gap-analyzer/build/index.test.d.ts.map +1 -0
  229. package/tools/test-gap-analyzer/build/index.test.js.map +1 -0
  230. package/tools/test-gap-analyzer/package.json +20 -0
  231. package/tools/typescript-enforcer/build/scanner.d.ts.map +1 -1
  232. package/tools/typescript-enforcer/build/scanner.js +13 -1
  233. package/tools/typescript-enforcer/build/scanner.js.map +1 -1
  234. package/tools/typescript-enforcer/build/types.d.ts +1 -0
  235. package/tools/typescript-enforcer/build/types.d.ts.map +1 -1
@@ -0,0 +1,329 @@
1
+ // ============================================================================
2
+ // TOOL #9: detect-anti-patterns
3
+ // Detects prop drilling, tight coupling, duplicated logic, large utility files,
4
+ // god components, inline JSX callbacks, a11y violations, bad fetch patterns
5
+ // ============================================================================
6
+ import * as path from 'path';
7
+ import { findSourceFiles, readFileContent, resolveSourceDir } from '../utils/file-scanner.js';
8
+ import { analyzeComponent, parseFile, extractFunctions } from '../utils/ast-parser.js';
9
+ import { buildImportGraph, calculateCoupling } from '../utils/import-tracker.js';
10
+ import { DEFAULT_CONFIG } from '../types.js';
11
+ export async function detectAntiPatterns(appPath, config) {
12
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
13
+ const srcPath = resolveSourceDir(appPath);
14
+ const files = await findSourceFiles(srcPath);
15
+ const graph = await buildImportGraph(appPath);
16
+ const antiPatterns = [];
17
+ // ── 1. Prop drilling ─────────────────────────────────────────────────────
18
+ const propDrillingFiles = [];
19
+ for (const file of files) {
20
+ const analysis = analyzeComponent(file);
21
+ if (!analysis)
22
+ continue;
23
+ if (analysis.props.length > 5) {
24
+ const content = readFileContent(file);
25
+ if (!content)
26
+ continue;
27
+ const spreadProps = content.match(/\.\.\.\w+Props/g) || [];
28
+ const passingProps = content.match(/<[A-Z]\w+[^>]*\w+=\{[^}]+\}/g) || [];
29
+ if (spreadProps.length > 0 && passingProps.length > 3) {
30
+ propDrillingFiles.push(path.relative(appPath, file));
31
+ }
32
+ }
33
+ }
34
+ if (propDrillingFiles.length > 0) {
35
+ antiPatterns.push({
36
+ type: 'prop-drilling',
37
+ description: `Components passing props through multiple levels (${propDrillingFiles.length} files). Consider using Context or state management.`,
38
+ files: propDrillingFiles.slice(0, 10),
39
+ });
40
+ }
41
+ // ── 2. Tight coupling ────────────────────────────────────────────────────
42
+ const tightCouplingPairs = [];
43
+ const processedPairs = new Set();
44
+ for (const fileA of files) {
45
+ for (const fileB of files) {
46
+ if (fileA === fileB)
47
+ continue;
48
+ const pairKey = [fileA, fileB].sort().join('|');
49
+ if (processedPairs.has(pairKey))
50
+ continue;
51
+ processedPairs.add(pairKey);
52
+ const coupling = calculateCoupling(graph, fileA, fileB);
53
+ if (coupling >= 3) {
54
+ tightCouplingPairs.push(`${path.relative(appPath, fileA)} <-> ${path.relative(appPath, fileB)} (coupling: ${coupling})`);
55
+ }
56
+ }
57
+ }
58
+ if (tightCouplingPairs.length > 0) {
59
+ antiPatterns.push({
60
+ type: 'tight-coupling',
61
+ description: 'Tightly coupled components detected. Consider extracting shared logic or using dependency injection.',
62
+ files: tightCouplingPairs.slice(0, 10),
63
+ });
64
+ }
65
+ // ── 3. Large utility files ───────────────────────────────────────────────
66
+ const utilFiles = files.filter((f) => {
67
+ const lower = f.toLowerCase();
68
+ return lower.includes('/util') || lower.includes('/helper') || lower.includes('/utils.');
69
+ });
70
+ for (const utilFile of utilFiles) {
71
+ const content = readFileContent(utilFile);
72
+ if (!content)
73
+ continue;
74
+ const lines = content.split('\n').length;
75
+ if (lines > mergedConfig.largeUtilLines) {
76
+ const parsed = parseFile(utilFile);
77
+ if (!parsed)
78
+ continue;
79
+ const functions = extractFunctions(parsed.ast);
80
+ antiPatterns.push({
81
+ type: 'large-utility-file',
82
+ description: `Utility file has ${lines} lines with ${functions.length} functions. Consider splitting into smaller modules.`,
83
+ files: [path.relative(appPath, utilFile)],
84
+ });
85
+ }
86
+ }
87
+ // ── 4. Duplicated logic patterns ─────────────────────────────────────────
88
+ const functionBodies = new Map();
89
+ for (const file of files) {
90
+ const content = readFileContent(file);
91
+ if (!content)
92
+ continue;
93
+ const patterns = [
94
+ { name: 'date-formatting', regex: /formatDate|dateFormat|toLocaleDateString|moment\(|dayjs\(/g },
95
+ { name: 'currency-formatting', regex: /formatCurrency|currencyFormat|Intl\.NumberFormat.*style.*currency/g },
96
+ { name: 'validation', regex: /validate\w+|isValid\w+|checkValid/g },
97
+ { name: 'api-error-handling', regex: /catch\s*\([^)]*\)\s*\{[^}]*error/g },
98
+ ];
99
+ for (const pattern of patterns) {
100
+ const matches = content.match(pattern.regex);
101
+ if (matches && matches.length > 0) {
102
+ if (!functionBodies.has(pattern.name))
103
+ functionBodies.set(pattern.name, []);
104
+ functionBodies.get(pattern.name).push(path.relative(appPath, file));
105
+ }
106
+ }
107
+ }
108
+ for (const [pattern, fileList] of functionBodies) {
109
+ if (fileList.length > 3) {
110
+ antiPatterns.push({
111
+ type: 'duplicated-logic',
112
+ description: `Repeated "${pattern}" pattern found in ${fileList.length} files. Consider extracting to a shared utility.`,
113
+ files: fileList.slice(0, 10),
114
+ });
115
+ }
116
+ }
117
+ // ── 5. God components ────────────────────────────────────────────────────
118
+ const godComponentFiles = [];
119
+ for (const file of files) {
120
+ const content = readFileContent(file);
121
+ if (!content)
122
+ continue;
123
+ const analysis = analyzeComponent(file);
124
+ const relPath = path.relative(appPath, file);
125
+ const responsibilities = new Set();
126
+ // Detect state via both AST (if available) and regex (for namespace style)
127
+ const hasUseState = analysis?.hooks.some((h) => h.name === 'useState') ||
128
+ /\buseState\s*\(|React\.useState\s*\(/.test(content);
129
+ const hasUseEffect = analysis?.hooks.some((h) => h.name === 'useEffect') ||
130
+ /\buseEffect\s*\(|React\.useEffect\s*\(/.test(content);
131
+ const hasUseReducer = analysis?.hooks.some((h) => h.name === 'useReducer') ||
132
+ /\buseReducer\s*\(|React\.useReducer\s*\(/.test(content);
133
+ if (hasUseState || hasUseReducer)
134
+ responsibilities.add('state');
135
+ if (hasUseEffect)
136
+ responsibilities.add('effects');
137
+ // API calls
138
+ if (content.includes('fetch(') || content.includes('axios.') || content.includes('.get(') || content.includes('.post(')) {
139
+ responsibilities.add('api-calls');
140
+ }
141
+ // Routing
142
+ const importSources = analysis?.imports.map((i) => i.source.toLowerCase()) || [];
143
+ if (importSources.some((s) => s.includes('router') || s.includes('navigate')))
144
+ responsibilities.add('routing');
145
+ // Store
146
+ if (importSources.some((s) => s.includes('redux') || s.includes('store') || s.includes('zustand')))
147
+ responsibilities.add('store');
148
+ // Form handling
149
+ if (content.includes('onSubmit') || content.includes('FormData') || content.includes('handleSubmit')) {
150
+ responsibilities.add('form-handling');
151
+ }
152
+ // Heavy rendering
153
+ const jsxElements = analysis?.jsxElements.length || (content.match(/<[A-Z][a-zA-Z]*/g) || []).length;
154
+ if (jsxElements > 15)
155
+ responsibilities.add('heavy-rendering');
156
+ // Count state variables
157
+ const stateVarCount = (content.match(/\buseState\s*\(/g) || []).length +
158
+ (content.match(/React\.useState\s*\(/g) || []).length;
159
+ const lines = content.split('\n').length;
160
+ // Lower thresholds: a 100-line component with 3+ responsibilities IS a god component
161
+ if (responsibilities.size >= 3 && (lines > 80 || stateVarCount >= 3)) {
162
+ const detail = `${lines} lines, ${responsibilities.size} responsibilities (${Array.from(responsibilities).join(', ')}), ${stateVarCount} state vars`;
163
+ godComponentFiles.push(`${relPath} — ${detail}`);
164
+ }
165
+ }
166
+ if (godComponentFiles.length > 0) {
167
+ antiPatterns.push({
168
+ type: 'god-component',
169
+ description: `God component(s) detected — too many responsibilities in a single file. Break into feature-specific sub-components.`,
170
+ files: godComponentFiles.slice(0, 10),
171
+ });
172
+ }
173
+ // ── 6. Inline JSX callbacks ───────────────────────────────────────────────
174
+ const inlineCallbackFiles = [];
175
+ for (const file of files) {
176
+ const content = readFileContent(file);
177
+ if (!content)
178
+ continue;
179
+ // Detect onClick/onChange/onSubmit/onBlur etc with inline arrow functions
180
+ const matches = [...content.matchAll(/on[A-Z]\w*=\{(?:\([^)]*\)|[a-z_]\w*)\s*=>/g)];
181
+ if (matches.length >= 2) {
182
+ inlineCallbackFiles.push({
183
+ file: path.relative(appPath, file),
184
+ count: matches.length,
185
+ examples: matches.slice(0, 2).map((m) => m[0].slice(0, 60)),
186
+ });
187
+ }
188
+ }
189
+ if (inlineCallbackFiles.length > 0) {
190
+ const total = inlineCallbackFiles.reduce((s, f) => s + f.count, 0);
191
+ antiPatterns.push({
192
+ type: 'inline-callbacks',
193
+ description: `${total} inline arrow function callbacks in JSX across ${inlineCallbackFiles.length} file(s). Each creates a new function reference on every render — extract to useCallback or named handlers.`,
194
+ files: inlineCallbackFiles.map((f) => `${f.file} (${f.count} callbacks)`).slice(0, 10),
195
+ });
196
+ }
197
+ // ── 7. Fetch/async without error handling ─────────────────────────────────
198
+ const noErrorHandlingFiles = [];
199
+ for (const file of files) {
200
+ const content = readFileContent(file);
201
+ if (!content)
202
+ continue;
203
+ const hasFetch = content.includes('fetch(') || content.includes('axios.');
204
+ if (!hasFetch)
205
+ continue;
206
+ const hasCatch = content.includes('.catch(') || content.includes('try {') || content.includes('try{');
207
+ if (!hasCatch) {
208
+ noErrorHandlingFiles.push(path.relative(appPath, file));
209
+ }
210
+ }
211
+ if (noErrorHandlingFiles.length > 0) {
212
+ antiPatterns.push({
213
+ type: 'missing-error-handling',
214
+ description: `${noErrorHandlingFiles.length} file(s) make API calls without try/catch or .catch(). Unhandled rejections will silently fail in production.`,
215
+ files: noErrorHandlingFiles.slice(0, 10),
216
+ });
217
+ }
218
+ // ── 8. React namespace style (React.useState instead of imported) ─────────
219
+ const namespacedReactFiles = [];
220
+ for (const file of files) {
221
+ const content = readFileContent(file);
222
+ if (!content)
223
+ continue;
224
+ const namespacedHooks = (content.match(/React\.(useState|useEffect|useCallback|useMemo|useRef|useContext|useReducer)\s*\(/g) || []);
225
+ if (namespacedHooks.length >= 2) {
226
+ namespacedReactFiles.push(`${path.relative(appPath, file)} (${namespacedHooks.length} namespace calls)`);
227
+ }
228
+ }
229
+ if (namespacedReactFiles.length > 0) {
230
+ antiPatterns.push({
231
+ type: 'react-namespace-style',
232
+ description: `Files use React.useState/useEffect/etc instead of named imports. Prefer: import { useState, useEffect } from 'react'`,
233
+ files: namespacedReactFiles.slice(0, 10),
234
+ });
235
+ }
236
+ // ── 9. Images without alt text ────────────────────────────────────────────
237
+ const noAltFiles = [];
238
+ for (const file of files) {
239
+ const content = readFileContent(file);
240
+ if (!content)
241
+ continue;
242
+ // Find <img> tags that don't have alt=
243
+ const imgMatches = [...content.matchAll(/<img\b([^>]*)>/g)];
244
+ const noAltCount = imgMatches.filter((m) => !m[1].includes('alt=')).length;
245
+ if (noAltCount > 0) {
246
+ noAltFiles.push({ file: path.relative(appPath, file), count: noAltCount });
247
+ }
248
+ }
249
+ if (noAltFiles.length > 0) {
250
+ const total = noAltFiles.reduce((s, f) => s + f.count, 0);
251
+ antiPatterns.push({
252
+ type: 'missing-alt-text',
253
+ description: `${total} <img> element(s) missing alt attribute across ${noAltFiles.length} file(s). Required for accessibility (WCAG 2.1 1.1.1).`,
254
+ files: noAltFiles.map((f) => `${f.file} (${f.count} images)`).slice(0, 10),
255
+ });
256
+ }
257
+ // ── 10. Non-semantic HTML structure ──────────────────────────────────────
258
+ const nonSemanticFiles = [];
259
+ const semanticDivPatterns = [
260
+ /className=["'`]header["'`]/,
261
+ /className=["'`]footer["'`]/,
262
+ /className=["'`]main["'`]/,
263
+ /className=["'`]nav["'`]/,
264
+ /className=["'`]navigation["'`]/,
265
+ /className=["'`]sidebar["'`]/,
266
+ /className=["'`]article["'`]/,
267
+ /className=["'`]section["'`]/,
268
+ ];
269
+ for (const file of files) {
270
+ const content = readFileContent(file);
271
+ if (!content)
272
+ continue;
273
+ const hasNonSemantic = semanticDivPatterns.some((p) => p.test(content));
274
+ if (hasNonSemantic) {
275
+ // Confirm it's using div, not the semantic element itself
276
+ const hasDivHeader = content.match(/<div\b[^>]*className=["'`](?:header|footer|main|nav|sidebar|article|section)["'`]/);
277
+ if (hasDivHeader) {
278
+ nonSemanticFiles.push(path.relative(appPath, file));
279
+ }
280
+ }
281
+ }
282
+ if (nonSemanticFiles.length > 0) {
283
+ antiPatterns.push({
284
+ type: 'non-semantic-html',
285
+ description: `${nonSemanticFiles.length} file(s) use <div className="header/footer/main/nav"> instead of semantic HTML elements (<header>, <footer>, <main>, <nav>). Hurts SEO and accessibility.`,
286
+ files: nonSemanticFiles.slice(0, 10),
287
+ });
288
+ }
289
+ // ── 11. Form inputs without labels ───────────────────────────────────────
290
+ const noLabelFiles = [];
291
+ for (const file of files) {
292
+ const content = readFileContent(file);
293
+ if (!content)
294
+ continue;
295
+ const inputs = [...content.matchAll(/<input\b([^>]*)>/g)];
296
+ const hasLabel = content.includes('<label') || content.includes('htmlFor') || content.includes('aria-label');
297
+ if (inputs.length > 0 && !hasLabel) {
298
+ noLabelFiles.push(path.relative(appPath, file));
299
+ }
300
+ }
301
+ if (noLabelFiles.length > 0) {
302
+ antiPatterns.push({
303
+ type: 'unlabeled-inputs',
304
+ description: `${noLabelFiles.length} file(s) have form <input> elements without associated <label> or aria-label. Screen readers cannot identify these fields (WCAG 2.1 1.3.1).`,
305
+ files: noLabelFiles.slice(0, 10),
306
+ });
307
+ }
308
+ // ── 12. Placeholder/external images in production code ───────────────────
309
+ const placeholderImageFiles = [];
310
+ for (const file of files) {
311
+ const content = readFileContent(file);
312
+ if (!content)
313
+ continue;
314
+ if (content.includes('via.placeholder.com') || content.includes('placeholder.com') || content.includes('placehold.it') || content.includes('dummyimage.com')) {
315
+ placeholderImageFiles.push(path.relative(appPath, file));
316
+ }
317
+ }
318
+ if (placeholderImageFiles.length > 0) {
319
+ antiPatterns.push({
320
+ type: 'placeholder-images',
321
+ description: `${placeholderImageFiles.length} file(s) use external placeholder image services. Replace with real assets before production.`,
322
+ files: placeholderImageFiles.slice(0, 10),
323
+ });
324
+ }
325
+ return {
326
+ antiPatterns,
327
+ };
328
+ }
329
+ //# sourceMappingURL=09-detect-anti-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"09-detect-anti-patterns.js","sourceRoot":"","sources":["../../src/tools/09-detect-anti-patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DetectDuplicationOutput, AnalyzerConfig } from '../types.js';
2
+ export declare function detectDuplication(appPath: string, config?: Partial<AnalyzerConfig>): Promise<DetectDuplicationOutput>;
3
+ //# sourceMappingURL=10-detect-duplication.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"10-detect-duplication.d.ts","sourceRoot":"","sources":["../../src/tools/10-detect-duplication.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,uBAAuB,EAAiB,cAAc,EAAE,MAAM,aAAa,CAAC;AAiD1F,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAwJ3H"}
@@ -0,0 +1,192 @@
1
+ // ============================================================================
2
+ // TOOL #10: detect-duplication
3
+ // Detects duplicate components, utility functions, similar file structures
4
+ // ============================================================================
5
+ import * as path from 'path';
6
+ import { findSourceFiles, readFileContent, resolveSourceDir } from '../utils/file-scanner.js';
7
+ import { analyzeComponent, parseFile, extractFunctions } from '../utils/ast-parser.js';
8
+ import { DEFAULT_CONFIG } from '../types.js';
9
+ /**
10
+ * Tokenize code into meaningful tokens for comparison
11
+ */
12
+ function tokenize(code) {
13
+ return code
14
+ .replace(/\/\/.*$/gm, '')
15
+ .replace(/\/\*[\s\S]*?\*\//g, '')
16
+ .replace(/['"`][^'"`]*['"`]/g, 'STR') // normalize string literals
17
+ .replace(/\b\d+\b/g, 'NUM') // normalize numbers
18
+ .match(/\b\w+\b|[{}()[\]=><]/g) ?? [];
19
+ }
20
+ /**
21
+ * Jaccard similarity on token sets (0-1). Much more accurate than char-position matching.
22
+ * Strips import statements before comparison — shared imports inflate similarity.
23
+ */
24
+ function calculateSimilarity(a, b) {
25
+ if (!a || !b)
26
+ return 0;
27
+ if (a === b)
28
+ return 1;
29
+ // Strip import lines so shared boilerplate doesn't inflate score
30
+ const stripImports = (s) => s.replace(/^import\s[^\n]+\n/gm, '');
31
+ const tokensA = new Set(tokenize(stripImports(a)));
32
+ const tokensB = new Set(tokenize(stripImports(b)));
33
+ if (tokensA.size === 0 || tokensB.size === 0)
34
+ return 0;
35
+ let intersection = 0;
36
+ for (const t of tokensA) {
37
+ if (tokensB.has(t))
38
+ intersection++;
39
+ }
40
+ const union = tokensA.size + tokensB.size - intersection;
41
+ return union === 0 ? 1 : intersection / union;
42
+ }
43
+ /**
44
+ * Normalize code for comparison (remove whitespace, comments)
45
+ */
46
+ function normalizeCode(code) {
47
+ return code
48
+ .replace(/\/\/.*$/gm, '')
49
+ .replace(/\/\*[\s\S]*?\*\//g, '')
50
+ .replace(/\s+/g, ' ')
51
+ .trim();
52
+ }
53
+ export async function detectDuplication(appPath, config) {
54
+ const mergedConfig = { ...DEFAULT_CONFIG, ...config };
55
+ const srcPath = resolveSourceDir(appPath);
56
+ const files = await findSourceFiles(srcPath);
57
+ const duplicateComponents = [];
58
+ const duplicateUtils = [];
59
+ const isTestOrMockFile = (f) => {
60
+ const lower = f.toLowerCase();
61
+ return lower.includes('.test.') || lower.includes('.spec.') || lower.includes('.stories.') ||
62
+ lower.includes('/__tests__/') || lower.includes('/mocks/') || lower.includes('/mock/') ||
63
+ lower.includes('/fixtures/') || lower.includes('/testing/');
64
+ };
65
+ // Group files by type, excluding test/mock files from duplication analysis
66
+ const componentFiles = files.filter((f) => {
67
+ if (isTestOrMockFile(f))
68
+ return false;
69
+ const basename = path.basename(f, path.extname(f));
70
+ return /^[A-Z]/.test(basename);
71
+ });
72
+ const utilFiles = files.filter((f) => {
73
+ if (isTestOrMockFile(f))
74
+ return false;
75
+ const lower = f.toLowerCase();
76
+ return lower.includes('/util') || lower.includes('/helper') || lower.includes('/utils.');
77
+ });
78
+ // 1. Detect duplicate components by comparing structure
79
+ const componentAnalyses = new Map();
80
+ for (const file of componentFiles) {
81
+ const analysis = analyzeComponent(file);
82
+ if (analysis) {
83
+ componentAnalyses.set(file, analysis);
84
+ }
85
+ }
86
+ // Compare each pair of components
87
+ const processedComponentPairs = new Set();
88
+ for (const [fileA, analysisA] of componentAnalyses) {
89
+ for (const [fileB, analysisB] of componentAnalyses) {
90
+ if (fileA === fileB)
91
+ continue;
92
+ const pairKey = [fileA, fileB].sort().join('|');
93
+ if (processedComponentPairs.has(pairKey))
94
+ continue;
95
+ processedComponentPairs.add(pairKey);
96
+ // Compare based on: same hooks, similar imports, similar JSX structure
97
+ const hooksA = new Set(analysisA.hooks.map((h) => h.name));
98
+ const hooksB = new Set(analysisB.hooks.map((h) => h.name));
99
+ const hooksMatch = hooksA.size > 0 &&
100
+ Array.from(hooksA).every((h) => hooksB.has(h)) &&
101
+ hooksA.size === hooksB.size;
102
+ // Only compare hooks if both have at least 2 hooks (1 hook = too common to signal duplication)
103
+ if (hooksA.size < 2 || hooksB.size < 2)
104
+ continue;
105
+ // Compare normalized code
106
+ const contentA = readFileContent(fileA);
107
+ const contentB = readFileContent(fileB);
108
+ if (!contentA || !contentB)
109
+ continue;
110
+ // Skip tiny files (< 20 lines) — too small to constitute meaningful duplication
111
+ const linesA = contentA.split('\n').length;
112
+ const linesB = contentB.split('\n').length;
113
+ if (linesA < 20 || linesB < 20)
114
+ continue;
115
+ const normalizedA = normalizeCode(contentA);
116
+ const normalizedB = normalizeCode(contentB);
117
+ const similarity = calculateSimilarity(normalizedA, normalizedB);
118
+ if (hooksMatch && similarity > mergedConfig.duplicationThreshold) {
119
+ // Check if we already have an entry for either file
120
+ const existingEntry = duplicateComponents.find((d) => d.locations.includes(path.relative(appPath, fileA)) || d.locations.includes(path.relative(appPath, fileB)));
121
+ if (existingEntry) {
122
+ if (!existingEntry.locations.includes(path.relative(appPath, fileA))) {
123
+ existingEntry.locations.push(path.relative(appPath, fileA));
124
+ }
125
+ if (!existingEntry.locations.includes(path.relative(appPath, fileB))) {
126
+ existingEntry.locations.push(path.relative(appPath, fileB));
127
+ }
128
+ existingEntry.similarity = Math.max(existingEntry.similarity, similarity);
129
+ }
130
+ else {
131
+ duplicateComponents.push({
132
+ name: analysisA.name,
133
+ locations: [
134
+ path.relative(appPath, fileA),
135
+ path.relative(appPath, fileB),
136
+ ],
137
+ similarity: Math.round(similarity * 100) / 100,
138
+ });
139
+ }
140
+ }
141
+ }
142
+ }
143
+ // 2. Detect duplicate utility functions
144
+ const functionSignatures = new Map();
145
+ for (const file of utilFiles) {
146
+ const parsed = parseFile(file);
147
+ if (!parsed)
148
+ continue;
149
+ const functions = extractFunctions(parsed.ast);
150
+ for (const fn of functions) {
151
+ const signature = `${fn.name}(${fn.params.join(',')})`;
152
+ if (!functionSignatures.has(signature)) {
153
+ functionSignatures.set(signature, []);
154
+ }
155
+ functionSignatures.get(signature).push({
156
+ name: fn.name,
157
+ file: path.relative(appPath, file),
158
+ });
159
+ }
160
+ }
161
+ // Find duplicate function names across files
162
+ for (const [signature, locations] of functionSignatures) {
163
+ const uniqueFiles = new Set(locations.map((l) => l.file));
164
+ if (uniqueFiles.size > 1) {
165
+ const funcName = signature.split('(')[0];
166
+ duplicateUtils.push({
167
+ name: funcName,
168
+ locations: Array.from(uniqueFiles),
169
+ similarity: 1, // Same name = exact match
170
+ });
171
+ }
172
+ }
173
+ // 3. Detect feature modules with identical file layout (informational — not counted as component duplication)
174
+ // Feature-based architectures INTENTIONALLY have folders with the same structure (index.tsx, types.ts, etc.)
175
+ // We report this as a note, not as a duplication issue, to avoid penalizing good architecture.
176
+ const folderPatterns = new Map();
177
+ for (const file of files) {
178
+ const dir = path.dirname(path.relative(srcPath, file));
179
+ if (!folderPatterns.has(dir)) {
180
+ folderPatterns.set(dir, []);
181
+ }
182
+ folderPatterns.get(dir).push(path.basename(file));
183
+ }
184
+ // Only flag when CONTENT is actually duplicated (exact same normalized code), not just same filenames
185
+ // This avoids false positives for feature-based architectures like bulletproof-react or rowy
186
+ // (folder structure similarity is good practice, not duplication)
187
+ return {
188
+ duplicateComponents,
189
+ duplicateUtils: duplicateUtils.slice(0, 20), // Limit output
190
+ };
191
+ }
192
+ //# sourceMappingURL=10-detect-duplication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"10-detect-duplication.js","sourceRoot":"","sources":["../../src/tools/10-detect-duplication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AnalyzeDependenciesOutput, AnalyzerConfig } from '../types.js';
2
+ export declare function analyzeDependenciesUsage(appPath: string, config?: Partial<AnalyzerConfig>): Promise<AnalyzeDependenciesOutput>;
3
+ //# sourceMappingURL=11-analyze-dependencies-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"11-analyze-dependencies-usage.d.ts","sourceRoot":"","sources":["../../src/tools/11-analyze-dependencies-usage.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAwB,cAAc,EAAE,MAAM,aAAa,CAAC;AAwDnG,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAwMpI"}