driftdetect-detectors 0.1.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 (509) hide show
  1. package/dist/accessibility/alt-text.d.ts +63 -0
  2. package/dist/accessibility/alt-text.d.ts.map +1 -0
  3. package/dist/accessibility/alt-text.js +100 -0
  4. package/dist/accessibility/alt-text.js.map +1 -0
  5. package/dist/accessibility/aria-roles.d.ts +65 -0
  6. package/dist/accessibility/aria-roles.d.ts.map +1 -0
  7. package/dist/accessibility/aria-roles.js +87 -0
  8. package/dist/accessibility/aria-roles.js.map +1 -0
  9. package/dist/accessibility/focus-management.d.ts +62 -0
  10. package/dist/accessibility/focus-management.d.ts.map +1 -0
  11. package/dist/accessibility/focus-management.js +88 -0
  12. package/dist/accessibility/focus-management.js.map +1 -0
  13. package/dist/accessibility/heading-hierarchy.d.ts +66 -0
  14. package/dist/accessibility/heading-hierarchy.d.ts.map +1 -0
  15. package/dist/accessibility/heading-hierarchy.js +94 -0
  16. package/dist/accessibility/heading-hierarchy.js.map +1 -0
  17. package/dist/accessibility/index.d.ts +25 -0
  18. package/dist/accessibility/index.d.ts.map +1 -0
  19. package/dist/accessibility/index.js +21 -0
  20. package/dist/accessibility/index.js.map +1 -0
  21. package/dist/accessibility/keyboard-nav.d.ts +63 -0
  22. package/dist/accessibility/keyboard-nav.d.ts.map +1 -0
  23. package/dist/accessibility/keyboard-nav.js +86 -0
  24. package/dist/accessibility/keyboard-nav.js.map +1 -0
  25. package/dist/accessibility/semantic-html.d.ts +76 -0
  26. package/dist/accessibility/semantic-html.d.ts.map +1 -0
  27. package/dist/accessibility/semantic-html.js +204 -0
  28. package/dist/accessibility/semantic-html.js.map +1 -0
  29. package/dist/api/client-patterns.d.ts +121 -0
  30. package/dist/api/client-patterns.d.ts.map +1 -0
  31. package/dist/api/client-patterns.js +478 -0
  32. package/dist/api/client-patterns.js.map +1 -0
  33. package/dist/api/error-format.d.ts +140 -0
  34. package/dist/api/error-format.d.ts.map +1 -0
  35. package/dist/api/error-format.js +614 -0
  36. package/dist/api/error-format.js.map +1 -0
  37. package/dist/api/http-methods.d.ts +255 -0
  38. package/dist/api/http-methods.d.ts.map +1 -0
  39. package/dist/api/http-methods.js +890 -0
  40. package/dist/api/http-methods.js.map +1 -0
  41. package/dist/api/index.d.ts +16 -0
  42. package/dist/api/index.d.ts.map +1 -0
  43. package/dist/api/index.js +37 -0
  44. package/dist/api/index.js.map +1 -0
  45. package/dist/api/pagination.d.ts +133 -0
  46. package/dist/api/pagination.d.ts.map +1 -0
  47. package/dist/api/pagination.js +521 -0
  48. package/dist/api/pagination.js.map +1 -0
  49. package/dist/api/response-envelope.d.ts +261 -0
  50. package/dist/api/response-envelope.d.ts.map +1 -0
  51. package/dist/api/response-envelope.js +1050 -0
  52. package/dist/api/response-envelope.js.map +1 -0
  53. package/dist/api/retry-patterns.d.ts +117 -0
  54. package/dist/api/retry-patterns.d.ts.map +1 -0
  55. package/dist/api/retry-patterns.js +480 -0
  56. package/dist/api/retry-patterns.js.map +1 -0
  57. package/dist/api/route-structure.d.ts +128 -0
  58. package/dist/api/route-structure.d.ts.map +1 -0
  59. package/dist/api/route-structure.js +738 -0
  60. package/dist/api/route-structure.js.map +1 -0
  61. package/dist/auth/audit-logging.d.ts +80 -0
  62. package/dist/auth/audit-logging.d.ts.map +1 -0
  63. package/dist/auth/audit-logging.js +370 -0
  64. package/dist/auth/audit-logging.js.map +1 -0
  65. package/dist/auth/index.d.ts +33 -0
  66. package/dist/auth/index.d.ts.map +1 -0
  67. package/dist/auth/index.js +49 -0
  68. package/dist/auth/index.js.map +1 -0
  69. package/dist/auth/middleware-usage.d.ts +65 -0
  70. package/dist/auth/middleware-usage.d.ts.map +1 -0
  71. package/dist/auth/middleware-usage.js +192 -0
  72. package/dist/auth/middleware-usage.js.map +1 -0
  73. package/dist/auth/permission-checks.d.ts +60 -0
  74. package/dist/auth/permission-checks.d.ts.map +1 -0
  75. package/dist/auth/permission-checks.js +159 -0
  76. package/dist/auth/permission-checks.js.map +1 -0
  77. package/dist/auth/rbac-patterns.d.ts +68 -0
  78. package/dist/auth/rbac-patterns.d.ts.map +1 -0
  79. package/dist/auth/rbac-patterns.js +143 -0
  80. package/dist/auth/rbac-patterns.js.map +1 -0
  81. package/dist/auth/resource-ownership.d.ts +77 -0
  82. package/dist/auth/resource-ownership.d.ts.map +1 -0
  83. package/dist/auth/resource-ownership.js +324 -0
  84. package/dist/auth/resource-ownership.js.map +1 -0
  85. package/dist/auth/token-handling.d.ts +64 -0
  86. package/dist/auth/token-handling.d.ts.map +1 -0
  87. package/dist/auth/token-handling.js +151 -0
  88. package/dist/auth/token-handling.js.map +1 -0
  89. package/dist/base/ast-detector.d.ts +421 -0
  90. package/dist/base/ast-detector.d.ts.map +1 -0
  91. package/dist/base/ast-detector.js +699 -0
  92. package/dist/base/ast-detector.js.map +1 -0
  93. package/dist/base/base-detector.d.ts +366 -0
  94. package/dist/base/base-detector.d.ts.map +1 -0
  95. package/dist/base/base-detector.js +170 -0
  96. package/dist/base/base-detector.js.map +1 -0
  97. package/dist/base/index.d.ts +12 -0
  98. package/dist/base/index.d.ts.map +1 -0
  99. package/dist/base/index.js +17 -0
  100. package/dist/base/index.js.map +1 -0
  101. package/dist/base/regex-detector.d.ts +421 -0
  102. package/dist/base/regex-detector.d.ts.map +1 -0
  103. package/dist/base/regex-detector.js +537 -0
  104. package/dist/base/regex-detector.js.map +1 -0
  105. package/dist/base/structural-detector.d.ts +424 -0
  106. package/dist/base/structural-detector.d.ts.map +1 -0
  107. package/dist/base/structural-detector.js +731 -0
  108. package/dist/base/structural-detector.js.map +1 -0
  109. package/dist/base/types.d.ts +53 -0
  110. package/dist/base/types.d.ts.map +1 -0
  111. package/dist/base/types.js +5 -0
  112. package/dist/base/types.js.map +1 -0
  113. package/dist/components/component-structure.d.ts +163 -0
  114. package/dist/components/component-structure.d.ts.map +1 -0
  115. package/dist/components/component-structure.js +500 -0
  116. package/dist/components/component-structure.js.map +1 -0
  117. package/dist/components/composition.d.ts +287 -0
  118. package/dist/components/composition.d.ts.map +1 -0
  119. package/dist/components/composition.js +1123 -0
  120. package/dist/components/composition.js.map +1 -0
  121. package/dist/components/duplicate-detection.d.ts +251 -0
  122. package/dist/components/duplicate-detection.d.ts.map +1 -0
  123. package/dist/components/duplicate-detection.js +804 -0
  124. package/dist/components/duplicate-detection.js.map +1 -0
  125. package/dist/components/index.d.ts +16 -0
  126. package/dist/components/index.d.ts.map +1 -0
  127. package/dist/components/index.js +51 -0
  128. package/dist/components/index.js.map +1 -0
  129. package/dist/components/near-duplicate.d.ts +402 -0
  130. package/dist/components/near-duplicate.d.ts.map +1 -0
  131. package/dist/components/near-duplicate.js +1090 -0
  132. package/dist/components/near-duplicate.js.map +1 -0
  133. package/dist/components/props-patterns.d.ts +194 -0
  134. package/dist/components/props-patterns.d.ts.map +1 -0
  135. package/dist/components/props-patterns.js +795 -0
  136. package/dist/components/props-patterns.js.map +1 -0
  137. package/dist/components/ref-forwarding.d.ts +250 -0
  138. package/dist/components/ref-forwarding.d.ts.map +1 -0
  139. package/dist/components/ref-forwarding.js +832 -0
  140. package/dist/components/ref-forwarding.js.map +1 -0
  141. package/dist/components/state-patterns.d.ts +291 -0
  142. package/dist/components/state-patterns.d.ts.map +1 -0
  143. package/dist/components/state-patterns.js +970 -0
  144. package/dist/components/state-patterns.js.map +1 -0
  145. package/dist/config/config-validation.d.ts +74 -0
  146. package/dist/config/config-validation.d.ts.map +1 -0
  147. package/dist/config/config-validation.js +446 -0
  148. package/dist/config/config-validation.js.map +1 -0
  149. package/dist/config/default-values.d.ts +72 -0
  150. package/dist/config/default-values.d.ts.map +1 -0
  151. package/dist/config/default-values.js +386 -0
  152. package/dist/config/default-values.js.map +1 -0
  153. package/dist/config/env-naming.d.ts +73 -0
  154. package/dist/config/env-naming.d.ts.map +1 -0
  155. package/dist/config/env-naming.js +429 -0
  156. package/dist/config/env-naming.js.map +1 -0
  157. package/dist/config/environment-detection.d.ts +72 -0
  158. package/dist/config/environment-detection.d.ts.map +1 -0
  159. package/dist/config/environment-detection.js +400 -0
  160. package/dist/config/environment-detection.js.map +1 -0
  161. package/dist/config/feature-flags.d.ts +72 -0
  162. package/dist/config/feature-flags.d.ts.map +1 -0
  163. package/dist/config/feature-flags.js +384 -0
  164. package/dist/config/feature-flags.js.map +1 -0
  165. package/dist/config/index.d.ts +27 -0
  166. package/dist/config/index.d.ts.map +1 -0
  167. package/dist/config/index.js +43 -0
  168. package/dist/config/index.js.map +1 -0
  169. package/dist/config/required-optional.d.ts +71 -0
  170. package/dist/config/required-optional.d.ts.map +1 -0
  171. package/dist/config/required-optional.js +344 -0
  172. package/dist/config/required-optional.js.map +1 -0
  173. package/dist/data-access/connection-pooling.d.ts +63 -0
  174. package/dist/data-access/connection-pooling.d.ts.map +1 -0
  175. package/dist/data-access/connection-pooling.js +297 -0
  176. package/dist/data-access/connection-pooling.js.map +1 -0
  177. package/dist/data-access/dto-patterns.d.ts +64 -0
  178. package/dist/data-access/dto-patterns.d.ts.map +1 -0
  179. package/dist/data-access/dto-patterns.js +291 -0
  180. package/dist/data-access/dto-patterns.js.map +1 -0
  181. package/dist/data-access/index.d.ts +31 -0
  182. package/dist/data-access/index.d.ts.map +1 -0
  183. package/dist/data-access/index.js +49 -0
  184. package/dist/data-access/index.js.map +1 -0
  185. package/dist/data-access/n-plus-one.d.ts +60 -0
  186. package/dist/data-access/n-plus-one.d.ts.map +1 -0
  187. package/dist/data-access/n-plus-one.js +264 -0
  188. package/dist/data-access/n-plus-one.js.map +1 -0
  189. package/dist/data-access/query-patterns.d.ts +64 -0
  190. package/dist/data-access/query-patterns.d.ts.map +1 -0
  191. package/dist/data-access/query-patterns.js +314 -0
  192. package/dist/data-access/query-patterns.js.map +1 -0
  193. package/dist/data-access/repository-pattern.d.ts +62 -0
  194. package/dist/data-access/repository-pattern.d.ts.map +1 -0
  195. package/dist/data-access/repository-pattern.js +257 -0
  196. package/dist/data-access/repository-pattern.js.map +1 -0
  197. package/dist/data-access/transaction-patterns.d.ts +61 -0
  198. package/dist/data-access/transaction-patterns.d.ts.map +1 -0
  199. package/dist/data-access/transaction-patterns.js +277 -0
  200. package/dist/data-access/transaction-patterns.js.map +1 -0
  201. package/dist/data-access/validation-patterns.d.ts +62 -0
  202. package/dist/data-access/validation-patterns.d.ts.map +1 -0
  203. package/dist/data-access/validation-patterns.js +301 -0
  204. package/dist/data-access/validation-patterns.js.map +1 -0
  205. package/dist/documentation/deprecation.d.ts +62 -0
  206. package/dist/documentation/deprecation.d.ts.map +1 -0
  207. package/dist/documentation/deprecation.js +83 -0
  208. package/dist/documentation/deprecation.js.map +1 -0
  209. package/dist/documentation/example-code.d.ts +64 -0
  210. package/dist/documentation/example-code.d.ts.map +1 -0
  211. package/dist/documentation/example-code.js +79 -0
  212. package/dist/documentation/example-code.js.map +1 -0
  213. package/dist/documentation/index.d.ts +22 -0
  214. package/dist/documentation/index.d.ts.map +1 -0
  215. package/dist/documentation/index.js +19 -0
  216. package/dist/documentation/index.js.map +1 -0
  217. package/dist/documentation/jsdoc-patterns.d.ts +72 -0
  218. package/dist/documentation/jsdoc-patterns.d.ts.map +1 -0
  219. package/dist/documentation/jsdoc-patterns.js +92 -0
  220. package/dist/documentation/jsdoc-patterns.js.map +1 -0
  221. package/dist/documentation/readme-structure.d.ts +67 -0
  222. package/dist/documentation/readme-structure.d.ts.map +1 -0
  223. package/dist/documentation/readme-structure.js +76 -0
  224. package/dist/documentation/readme-structure.js.map +1 -0
  225. package/dist/documentation/todo-patterns.d.ts +67 -0
  226. package/dist/documentation/todo-patterns.d.ts.map +1 -0
  227. package/dist/documentation/todo-patterns.js +73 -0
  228. package/dist/documentation/todo-patterns.js.map +1 -0
  229. package/dist/errors/async-errors.d.ts +72 -0
  230. package/dist/errors/async-errors.d.ts.map +1 -0
  231. package/dist/errors/async-errors.js +214 -0
  232. package/dist/errors/async-errors.js.map +1 -0
  233. package/dist/errors/circuit-breaker.d.ts +53 -0
  234. package/dist/errors/circuit-breaker.d.ts.map +1 -0
  235. package/dist/errors/circuit-breaker.js +241 -0
  236. package/dist/errors/circuit-breaker.js.map +1 -0
  237. package/dist/errors/error-codes.d.ts +73 -0
  238. package/dist/errors/error-codes.d.ts.map +1 -0
  239. package/dist/errors/error-codes.js +211 -0
  240. package/dist/errors/error-codes.js.map +1 -0
  241. package/dist/errors/error-logging.d.ts +73 -0
  242. package/dist/errors/error-logging.d.ts.map +1 -0
  243. package/dist/errors/error-logging.js +256 -0
  244. package/dist/errors/error-logging.js.map +1 -0
  245. package/dist/errors/error-propagation.d.ts +73 -0
  246. package/dist/errors/error-propagation.d.ts.map +1 -0
  247. package/dist/errors/error-propagation.js +244 -0
  248. package/dist/errors/error-propagation.js.map +1 -0
  249. package/dist/errors/exception-hierarchy.d.ts +75 -0
  250. package/dist/errors/exception-hierarchy.d.ts.map +1 -0
  251. package/dist/errors/exception-hierarchy.js +259 -0
  252. package/dist/errors/exception-hierarchy.js.map +1 -0
  253. package/dist/errors/index.d.ts +31 -0
  254. package/dist/errors/index.d.ts.map +1 -0
  255. package/dist/errors/index.js +49 -0
  256. package/dist/errors/index.js.map +1 -0
  257. package/dist/errors/try-catch-placement.d.ts +73 -0
  258. package/dist/errors/try-catch-placement.d.ts.map +1 -0
  259. package/dist/errors/try-catch-placement.js +214 -0
  260. package/dist/errors/try-catch-placement.js.map +1 -0
  261. package/dist/index.d.ts +221 -0
  262. package/dist/index.d.ts.map +1 -0
  263. package/dist/index.js +245 -0
  264. package/dist/index.js.map +1 -0
  265. package/dist/logging/context-fields.d.ts +48 -0
  266. package/dist/logging/context-fields.d.ts.map +1 -0
  267. package/dist/logging/context-fields.js +160 -0
  268. package/dist/logging/context-fields.js.map +1 -0
  269. package/dist/logging/correlation-ids.d.ts +44 -0
  270. package/dist/logging/correlation-ids.d.ts.map +1 -0
  271. package/dist/logging/correlation-ids.js +144 -0
  272. package/dist/logging/correlation-ids.js.map +1 -0
  273. package/dist/logging/health-checks.d.ts +45 -0
  274. package/dist/logging/health-checks.d.ts.map +1 -0
  275. package/dist/logging/health-checks.js +165 -0
  276. package/dist/logging/health-checks.js.map +1 -0
  277. package/dist/logging/index.d.ts +31 -0
  278. package/dist/logging/index.d.ts.map +1 -0
  279. package/dist/logging/index.js +49 -0
  280. package/dist/logging/index.js.map +1 -0
  281. package/dist/logging/log-levels.d.ts +46 -0
  282. package/dist/logging/log-levels.d.ts.map +1 -0
  283. package/dist/logging/log-levels.js +178 -0
  284. package/dist/logging/log-levels.js.map +1 -0
  285. package/dist/logging/metric-naming.d.ts +46 -0
  286. package/dist/logging/metric-naming.d.ts.map +1 -0
  287. package/dist/logging/metric-naming.js +157 -0
  288. package/dist/logging/metric-naming.js.map +1 -0
  289. package/dist/logging/pii-redaction.d.ts +44 -0
  290. package/dist/logging/pii-redaction.d.ts.map +1 -0
  291. package/dist/logging/pii-redaction.js +166 -0
  292. package/dist/logging/pii-redaction.js.map +1 -0
  293. package/dist/logging/structured-format.d.ts +53 -0
  294. package/dist/logging/structured-format.d.ts.map +1 -0
  295. package/dist/logging/structured-format.js +235 -0
  296. package/dist/logging/structured-format.js.map +1 -0
  297. package/dist/performance/bundle-size.d.ts +79 -0
  298. package/dist/performance/bundle-size.d.ts.map +1 -0
  299. package/dist/performance/bundle-size.js +276 -0
  300. package/dist/performance/bundle-size.js.map +1 -0
  301. package/dist/performance/caching-patterns.d.ts +78 -0
  302. package/dist/performance/caching-patterns.d.ts.map +1 -0
  303. package/dist/performance/caching-patterns.js +257 -0
  304. package/dist/performance/caching-patterns.js.map +1 -0
  305. package/dist/performance/code-splitting.d.ts +86 -0
  306. package/dist/performance/code-splitting.d.ts.map +1 -0
  307. package/dist/performance/code-splitting.js +447 -0
  308. package/dist/performance/code-splitting.js.map +1 -0
  309. package/dist/performance/debounce-throttle.d.ts +75 -0
  310. package/dist/performance/debounce-throttle.d.ts.map +1 -0
  311. package/dist/performance/debounce-throttle.js +232 -0
  312. package/dist/performance/debounce-throttle.js.map +1 -0
  313. package/dist/performance/index.d.ts +28 -0
  314. package/dist/performance/index.d.ts.map +1 -0
  315. package/dist/performance/index.js +39 -0
  316. package/dist/performance/index.js.map +1 -0
  317. package/dist/performance/lazy-loading.d.ts +75 -0
  318. package/dist/performance/lazy-loading.d.ts.map +1 -0
  319. package/dist/performance/lazy-loading.js +233 -0
  320. package/dist/performance/lazy-loading.js.map +1 -0
  321. package/dist/performance/memoization.d.ts +75 -0
  322. package/dist/performance/memoization.d.ts.map +1 -0
  323. package/dist/performance/memoization.js +251 -0
  324. package/dist/performance/memoization.js.map +1 -0
  325. package/dist/registry/detector-registry.d.ts +266 -0
  326. package/dist/registry/detector-registry.d.ts.map +1 -0
  327. package/dist/registry/detector-registry.js +526 -0
  328. package/dist/registry/detector-registry.js.map +1 -0
  329. package/dist/registry/index.d.ts +10 -0
  330. package/dist/registry/index.d.ts.map +1 -0
  331. package/dist/registry/index.js +10 -0
  332. package/dist/registry/index.js.map +1 -0
  333. package/dist/registry/loader.d.ts +232 -0
  334. package/dist/registry/loader.d.ts.map +1 -0
  335. package/dist/registry/loader.js +419 -0
  336. package/dist/registry/loader.js.map +1 -0
  337. package/dist/registry/types.d.ts +111 -0
  338. package/dist/registry/types.d.ts.map +1 -0
  339. package/dist/registry/types.js +19 -0
  340. package/dist/registry/types.js.map +1 -0
  341. package/dist/security/csp-headers.d.ts +78 -0
  342. package/dist/security/csp-headers.d.ts.map +1 -0
  343. package/dist/security/csp-headers.js +401 -0
  344. package/dist/security/csp-headers.js.map +1 -0
  345. package/dist/security/csrf-protection.d.ts +72 -0
  346. package/dist/security/csrf-protection.d.ts.map +1 -0
  347. package/dist/security/csrf-protection.js +344 -0
  348. package/dist/security/csrf-protection.js.map +1 -0
  349. package/dist/security/index.d.ts +30 -0
  350. package/dist/security/index.d.ts.map +1 -0
  351. package/dist/security/index.js +48 -0
  352. package/dist/security/index.js.map +1 -0
  353. package/dist/security/input-sanitization.d.ts +74 -0
  354. package/dist/security/input-sanitization.d.ts.map +1 -0
  355. package/dist/security/input-sanitization.js +373 -0
  356. package/dist/security/input-sanitization.js.map +1 -0
  357. package/dist/security/rate-limiting.d.ts +81 -0
  358. package/dist/security/rate-limiting.d.ts.map +1 -0
  359. package/dist/security/rate-limiting.js +535 -0
  360. package/dist/security/rate-limiting.js.map +1 -0
  361. package/dist/security/secret-management.d.ts +83 -0
  362. package/dist/security/secret-management.d.ts.map +1 -0
  363. package/dist/security/secret-management.js +547 -0
  364. package/dist/security/secret-management.js.map +1 -0
  365. package/dist/security/sql-injection.d.ts +76 -0
  366. package/dist/security/sql-injection.d.ts.map +1 -0
  367. package/dist/security/sql-injection.js +383 -0
  368. package/dist/security/sql-injection.js.map +1 -0
  369. package/dist/security/xss-prevention.d.ts +80 -0
  370. package/dist/security/xss-prevention.d.ts.map +1 -0
  371. package/dist/security/xss-prevention.js +416 -0
  372. package/dist/security/xss-prevention.js.map +1 -0
  373. package/dist/structural/barrel-exports.d.ts +178 -0
  374. package/dist/structural/barrel-exports.d.ts.map +1 -0
  375. package/dist/structural/barrel-exports.js +553 -0
  376. package/dist/structural/barrel-exports.js.map +1 -0
  377. package/dist/structural/circular-deps.d.ts +140 -0
  378. package/dist/structural/circular-deps.d.ts.map +1 -0
  379. package/dist/structural/circular-deps.js +422 -0
  380. package/dist/structural/circular-deps.js.map +1 -0
  381. package/dist/structural/co-location.d.ts +202 -0
  382. package/dist/structural/co-location.d.ts.map +1 -0
  383. package/dist/structural/co-location.js +640 -0
  384. package/dist/structural/co-location.js.map +1 -0
  385. package/dist/structural/directory-structure.d.ts +151 -0
  386. package/dist/structural/directory-structure.d.ts.map +1 -0
  387. package/dist/structural/directory-structure.js +457 -0
  388. package/dist/structural/directory-structure.js.map +1 -0
  389. package/dist/structural/file-naming.d.ts +61 -0
  390. package/dist/structural/file-naming.d.ts.map +1 -0
  391. package/dist/structural/file-naming.js +231 -0
  392. package/dist/structural/file-naming.js.map +1 -0
  393. package/dist/structural/import-ordering.d.ts +212 -0
  394. package/dist/structural/import-ordering.d.ts.map +1 -0
  395. package/dist/structural/import-ordering.js +821 -0
  396. package/dist/structural/import-ordering.js.map +1 -0
  397. package/dist/structural/index.d.ts +23 -0
  398. package/dist/structural/index.d.ts.map +1 -0
  399. package/dist/structural/index.js +26 -0
  400. package/dist/structural/index.js.map +1 -0
  401. package/dist/structural/module-boundaries.d.ts +164 -0
  402. package/dist/structural/module-boundaries.d.ts.map +1 -0
  403. package/dist/structural/module-boundaries.js +616 -0
  404. package/dist/structural/module-boundaries.js.map +1 -0
  405. package/dist/structural/package-boundaries.d.ts +182 -0
  406. package/dist/structural/package-boundaries.d.ts.map +1 -0
  407. package/dist/structural/package-boundaries.js +602 -0
  408. package/dist/structural/package-boundaries.js.map +1 -0
  409. package/dist/styling/class-naming.d.ts +263 -0
  410. package/dist/styling/class-naming.d.ts.map +1 -0
  411. package/dist/styling/class-naming.js +892 -0
  412. package/dist/styling/class-naming.js.map +1 -0
  413. package/dist/styling/color-usage.d.ts +213 -0
  414. package/dist/styling/color-usage.d.ts.map +1 -0
  415. package/dist/styling/color-usage.js +732 -0
  416. package/dist/styling/color-usage.js.map +1 -0
  417. package/dist/styling/design-tokens.d.ts +212 -0
  418. package/dist/styling/design-tokens.d.ts.map +1 -0
  419. package/dist/styling/design-tokens.js +748 -0
  420. package/dist/styling/design-tokens.js.map +1 -0
  421. package/dist/styling/index.d.ts +16 -0
  422. package/dist/styling/index.d.ts.map +1 -0
  423. package/dist/styling/index.js +56 -0
  424. package/dist/styling/index.js.map +1 -0
  425. package/dist/styling/responsive.d.ts +304 -0
  426. package/dist/styling/responsive.d.ts.map +1 -0
  427. package/dist/styling/responsive.js +888 -0
  428. package/dist/styling/responsive.js.map +1 -0
  429. package/dist/styling/spacing-scale.d.ts +248 -0
  430. package/dist/styling/spacing-scale.d.ts.map +1 -0
  431. package/dist/styling/spacing-scale.js +865 -0
  432. package/dist/styling/spacing-scale.js.map +1 -0
  433. package/dist/styling/tailwind-patterns.d.ts +305 -0
  434. package/dist/styling/tailwind-patterns.d.ts.map +1 -0
  435. package/dist/styling/tailwind-patterns.js +1181 -0
  436. package/dist/styling/tailwind-patterns.js.map +1 -0
  437. package/dist/styling/typography.d.ts +281 -0
  438. package/dist/styling/typography.d.ts.map +1 -0
  439. package/dist/styling/typography.js +1004 -0
  440. package/dist/styling/typography.js.map +1 -0
  441. package/dist/styling/z-index-scale.d.ts +270 -0
  442. package/dist/styling/z-index-scale.d.ts.map +1 -0
  443. package/dist/styling/z-index-scale.js +714 -0
  444. package/dist/styling/z-index-scale.js.map +1 -0
  445. package/dist/testing/co-location.d.ts +42 -0
  446. package/dist/testing/co-location.d.ts.map +1 -0
  447. package/dist/testing/co-location.js +134 -0
  448. package/dist/testing/co-location.js.map +1 -0
  449. package/dist/testing/describe-naming.d.ts +47 -0
  450. package/dist/testing/describe-naming.d.ts.map +1 -0
  451. package/dist/testing/describe-naming.js +150 -0
  452. package/dist/testing/describe-naming.js.map +1 -0
  453. package/dist/testing/file-naming.d.ts +44 -0
  454. package/dist/testing/file-naming.d.ts.map +1 -0
  455. package/dist/testing/file-naming.js +131 -0
  456. package/dist/testing/file-naming.js.map +1 -0
  457. package/dist/testing/fixture-patterns.d.ts +52 -0
  458. package/dist/testing/fixture-patterns.d.ts.map +1 -0
  459. package/dist/testing/fixture-patterns.js +228 -0
  460. package/dist/testing/fixture-patterns.js.map +1 -0
  461. package/dist/testing/index.d.ts +31 -0
  462. package/dist/testing/index.d.ts.map +1 -0
  463. package/dist/testing/index.js +49 -0
  464. package/dist/testing/index.js.map +1 -0
  465. package/dist/testing/mock-patterns.d.ts +53 -0
  466. package/dist/testing/mock-patterns.d.ts.map +1 -0
  467. package/dist/testing/mock-patterns.js +264 -0
  468. package/dist/testing/mock-patterns.js.map +1 -0
  469. package/dist/testing/setup-teardown.d.ts +55 -0
  470. package/dist/testing/setup-teardown.d.ts.map +1 -0
  471. package/dist/testing/setup-teardown.js +262 -0
  472. package/dist/testing/setup-teardown.js.map +1 -0
  473. package/dist/testing/test-structure.d.ts +51 -0
  474. package/dist/testing/test-structure.d.ts.map +1 -0
  475. package/dist/testing/test-structure.js +225 -0
  476. package/dist/testing/test-structure.js.map +1 -0
  477. package/dist/types/any-usage.d.ts +99 -0
  478. package/dist/types/any-usage.d.ts.map +1 -0
  479. package/dist/types/any-usage.js +641 -0
  480. package/dist/types/any-usage.js.map +1 -0
  481. package/dist/types/file-location.d.ts +76 -0
  482. package/dist/types/file-location.d.ts.map +1 -0
  483. package/dist/types/file-location.js +395 -0
  484. package/dist/types/file-location.js.map +1 -0
  485. package/dist/types/generic-patterns.d.ts +97 -0
  486. package/dist/types/generic-patterns.d.ts.map +1 -0
  487. package/dist/types/generic-patterns.js +615 -0
  488. package/dist/types/generic-patterns.js.map +1 -0
  489. package/dist/types/index.d.ts +31 -0
  490. package/dist/types/index.d.ts.map +1 -0
  491. package/dist/types/index.js +43 -0
  492. package/dist/types/index.js.map +1 -0
  493. package/dist/types/interface-vs-type.d.ts +81 -0
  494. package/dist/types/interface-vs-type.d.ts.map +1 -0
  495. package/dist/types/interface-vs-type.js +440 -0
  496. package/dist/types/interface-vs-type.js.map +1 -0
  497. package/dist/types/naming-conventions.d.ts +84 -0
  498. package/dist/types/naming-conventions.d.ts.map +1 -0
  499. package/dist/types/naming-conventions.js +455 -0
  500. package/dist/types/naming-conventions.js.map +1 -0
  501. package/dist/types/type-assertions.d.ts +98 -0
  502. package/dist/types/type-assertions.d.ts.map +1 -0
  503. package/dist/types/type-assertions.js +639 -0
  504. package/dist/types/type-assertions.js.map +1 -0
  505. package/dist/types/utility-types.d.ts +110 -0
  506. package/dist/types/utility-types.d.ts.map +1 -0
  507. package/dist/types/utility-types.js +547 -0
  508. package/dist/types/utility-types.js.map +1 -0
  509. package/package.json +44 -0
@@ -0,0 +1,1123 @@
1
+ /**
2
+ * Composition Detector - Component composition pattern detection
3
+ *
4
+ * Detects composition patterns including children prop usage, render props,
5
+ * Higher-Order Components (HOCs), compound components, slot-based composition,
6
+ * Provider/Consumer patterns, and controlled vs uncontrolled components.
7
+ *
8
+ * @requirements 8.6 - THE Component_Detector SHALL detect component composition patterns
9
+ */
10
+ import { ASTDetector } from '../base/index.js';
11
+ // ============================================================================
12
+ // Constants
13
+ // ============================================================================
14
+ /**
15
+ * Default configuration for composition pattern detection
16
+ */
17
+ export const DEFAULT_COMPOSITION_CONFIG = {
18
+ maxHOCDepth: 3,
19
+ maxRenderProps: 3,
20
+ maxContextNesting: 4,
21
+ detectControlledPatterns: true,
22
+ flagPropDrilling: true,
23
+ };
24
+ /**
25
+ * Common HOC patterns
26
+ */
27
+ export const HOC_PATTERNS = [
28
+ 'withRouter',
29
+ 'withStyles',
30
+ 'withTheme',
31
+ 'withAuth',
32
+ 'withLoading',
33
+ 'withErrorBoundary',
34
+ 'connect',
35
+ 'memo',
36
+ 'forwardRef',
37
+ 'observer',
38
+ ];
39
+ /**
40
+ * Common render prop names
41
+ */
42
+ export const RENDER_PROP_NAMES = [
43
+ 'render',
44
+ 'children',
45
+ 'renderItem',
46
+ 'renderHeader',
47
+ 'renderFooter',
48
+ 'renderEmpty',
49
+ 'renderLoading',
50
+ 'renderError',
51
+ 'component',
52
+ ];
53
+ /**
54
+ * Controlled component prop patterns
55
+ */
56
+ export const CONTROLLED_PROP_PATTERNS = [
57
+ { value: 'value', onChange: 'onChange' },
58
+ { value: 'checked', onChange: 'onChange' },
59
+ { value: 'selected', onChange: 'onSelect' },
60
+ { value: 'open', onChange: 'onOpenChange' },
61
+ { value: 'visible', onChange: 'onVisibleChange' },
62
+ { value: 'expanded', onChange: 'onExpandedChange' },
63
+ ];
64
+ /**
65
+ * Uncontrolled component prop patterns
66
+ */
67
+ export const UNCONTROLLED_PROP_PATTERNS = [
68
+ 'defaultValue',
69
+ 'defaultChecked',
70
+ 'defaultSelected',
71
+ 'defaultOpen',
72
+ 'defaultVisible',
73
+ 'defaultExpanded',
74
+ ];
75
+ // ============================================================================
76
+ // Helper Functions - Component Detection
77
+ // ============================================================================
78
+ /**
79
+ * Check if a node represents a React component
80
+ */
81
+ export function isReactComponent(node, content) {
82
+ if (node.type === 'function_declaration' ||
83
+ node.type === 'arrow_function' ||
84
+ node.type === 'function_expression') {
85
+ const name = getComponentName(node, content);
86
+ if (!name || !/^[A-Z]/.test(name)) {
87
+ return false;
88
+ }
89
+ const nodeText = node.text;
90
+ return nodeText.includes('<') && (nodeText.includes('/>') || nodeText.includes('</'));
91
+ }
92
+ return false;
93
+ }
94
+ /**
95
+ * Get the component name from a node
96
+ */
97
+ export function getComponentName(node, content) {
98
+ if (node.type === 'function_declaration') {
99
+ const nameNode = node.children.find(c => c.type === 'identifier');
100
+ return nameNode?.text;
101
+ }
102
+ const lines = content.split('\n');
103
+ const line = lines[node.startPosition.row];
104
+ if (line) {
105
+ const match = line.match(/(?:const|let|var|export\s+(?:const|let|var)?)\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/);
106
+ if (match && match[1]) {
107
+ return match[1];
108
+ }
109
+ }
110
+ return undefined;
111
+ }
112
+ // ============================================================================
113
+ // Helper Functions - Children Prop Detection
114
+ // ============================================================================
115
+ /**
116
+ * Detect children prop usage in a component
117
+ */
118
+ export function detectChildrenProp(nodeText) {
119
+ // Check for children in props destructuring
120
+ const hasChildrenDestructured = /\{\s*[^}]*\bchildren\b[^}]*\}/.test(nodeText);
121
+ // Check for props.children usage
122
+ const hasPropsChildren = /props\.children/.test(nodeText);
123
+ // Check for children being rendered
124
+ const rendersChildren = /\{children\}|\{props\.children\}/.test(nodeText);
125
+ // Check for React.Children usage
126
+ const usesReactChildren = /React\.Children\.|Children\./.test(nodeText);
127
+ if (hasChildrenDestructured || hasPropsChildren || rendersChildren || usesReactChildren) {
128
+ const line = nodeText.split('\n').findIndex(l => /children/.test(l)) + 1;
129
+ return {
130
+ pattern: 'children-prop',
131
+ componentName: '',
132
+ line,
133
+ column: 1,
134
+ details: {},
135
+ };
136
+ }
137
+ return null;
138
+ }
139
+ /**
140
+ * Check if component accepts children prop
141
+ */
142
+ export function acceptsChildrenProp(nodeText) {
143
+ // Check for children in type definition
144
+ const hasChildrenType = /children\s*[?:]?\s*:?\s*(?:React\.)?(?:ReactNode|ReactElement|JSX\.Element)/.test(nodeText);
145
+ // Check for PropsWithChildren
146
+ const hasPropsWithChildren = /PropsWithChildren/.test(nodeText);
147
+ // Check for children in destructuring or usage
148
+ const hasChildrenUsage = /\bchildren\b/.test(nodeText);
149
+ return hasChildrenType || hasPropsWithChildren || hasChildrenUsage;
150
+ }
151
+ // ============================================================================
152
+ // Helper Functions - Render Props Detection
153
+ // ============================================================================
154
+ /**
155
+ * Detect render props pattern in a component
156
+ */
157
+ export function detectRenderProps(nodeText) {
158
+ const renderPropNames = [];
159
+ // Check for render prop in props
160
+ for (const propName of RENDER_PROP_NAMES) {
161
+ // Match: render={...}, renderItem={...}, etc.
162
+ const propPattern = new RegExp(`\\b${propName}\\s*[=:]\\s*\\{?\\s*\\(?`, 'g');
163
+ if (propPattern.test(nodeText)) {
164
+ renderPropNames.push(propName);
165
+ }
166
+ }
167
+ // Check for children as function pattern
168
+ const childrenAsFunction = /children\s*\(\s*[^)]*\s*\)/.test(nodeText) ||
169
+ /\{children\s*&&\s*children\s*\(/.test(nodeText) ||
170
+ /typeof\s+children\s*===\s*['"]function['"]/.test(nodeText);
171
+ if (childrenAsFunction && !renderPropNames.includes('children')) {
172
+ renderPropNames.push('children');
173
+ }
174
+ // Check for render prop being called
175
+ const renderPropCalls = nodeText.match(/\b(render\w*)\s*\(\s*[^)]*\s*\)/g);
176
+ if (renderPropCalls) {
177
+ for (const call of renderPropCalls) {
178
+ const match = call.match(/\b(render\w*)\s*\(/);
179
+ if (match && match[1] && !renderPropNames.includes(match[1])) {
180
+ renderPropNames.push(match[1]);
181
+ }
182
+ }
183
+ }
184
+ if (renderPropNames.length > 0) {
185
+ const line = nodeText.split('\n').findIndex(l => renderPropNames.some(name => l.includes(name))) + 1;
186
+ return {
187
+ pattern: 'render-props',
188
+ componentName: '',
189
+ line,
190
+ column: 1,
191
+ details: {
192
+ renderPropNames,
193
+ },
194
+ };
195
+ }
196
+ return null;
197
+ }
198
+ // ============================================================================
199
+ // Helper Functions - HOC Detection
200
+ // ============================================================================
201
+ /**
202
+ * Detect Higher-Order Component pattern
203
+ */
204
+ export function detectHOC(content) {
205
+ const results = [];
206
+ // Pattern 1: export default withX(withY(Component))
207
+ const exportHOCPattern = /export\s+default\s+((?:with\w+|connect|memo|observer|forwardRef)\s*\([^)]*\))+/g;
208
+ let match;
209
+ while ((match = exportHOCPattern.exec(content)) !== null) {
210
+ const hocChain = match[1];
211
+ if (!hocChain)
212
+ continue;
213
+ const hocNames = extractHOCNames(hocChain);
214
+ if (hocNames.length > 0) {
215
+ const beforeMatch = content.slice(0, match.index);
216
+ const line = beforeMatch.split('\n').length;
217
+ results.push({
218
+ pattern: 'hoc',
219
+ componentName: '',
220
+ line,
221
+ column: 1,
222
+ details: {
223
+ hocNames,
224
+ nestingDepth: hocNames.length,
225
+ },
226
+ });
227
+ }
228
+ }
229
+ // Pattern 2: const EnhancedComponent = withX(Component)
230
+ const constHOCPattern = /const\s+([A-Z][a-zA-Z0-9]*)\s*=\s*((?:with\w+|connect|memo|observer|forwardRef)\s*\([^)]*\))+/g;
231
+ while ((match = constHOCPattern.exec(content)) !== null) {
232
+ const hocChain = match[2] || '';
233
+ const hocNames = extractHOCNames(hocChain);
234
+ if (hocNames.length > 0) {
235
+ const beforeMatch = content.slice(0, match.index);
236
+ const line = beforeMatch.split('\n').length;
237
+ results.push({
238
+ pattern: 'hoc',
239
+ componentName: match[1] || '',
240
+ line,
241
+ column: 1,
242
+ details: {
243
+ hocNames,
244
+ nestingDepth: hocNames.length,
245
+ },
246
+ });
247
+ }
248
+ }
249
+ // Pattern 3: Function that returns a component (HOC definition)
250
+ const hocDefinitionPattern = /(?:function|const)\s+with([A-Z][a-zA-Z0-9]*)\s*[=(<]/g;
251
+ while ((match = hocDefinitionPattern.exec(content)) !== null) {
252
+ const beforeMatch = content.slice(0, match.index);
253
+ const line = beforeMatch.split('\n').length;
254
+ results.push({
255
+ pattern: 'hoc',
256
+ componentName: `with${match[1]}`,
257
+ line,
258
+ column: 1,
259
+ details: {
260
+ hocNames: [`with${match[1]}`],
261
+ nestingDepth: 1,
262
+ },
263
+ });
264
+ }
265
+ return results;
266
+ }
267
+ /**
268
+ * Extract HOC names from a chain like withA(withB(Component))
269
+ */
270
+ export function extractHOCNames(hocChain) {
271
+ const names = [];
272
+ const pattern = /(with\w+|connect|memo|observer|forwardRef)\s*\(/g;
273
+ let match;
274
+ while ((match = pattern.exec(hocChain)) !== null) {
275
+ if (match[1]) {
276
+ names.push(match[1]);
277
+ }
278
+ }
279
+ return names;
280
+ }
281
+ // ============================================================================
282
+ // Helper Functions - Compound Component Detection
283
+ // ============================================================================
284
+ /**
285
+ * Detect compound component pattern
286
+ */
287
+ export function detectCompoundComponent(content) {
288
+ const results = [];
289
+ // Pattern 1: Component.SubComponent = ...
290
+ const subComponentPattern = /([A-Z][a-zA-Z0-9]*)\.([A-Z][a-zA-Z0-9]*)\s*=/g;
291
+ const subComponents = new Map();
292
+ let match;
293
+ while ((match = subComponentPattern.exec(content)) !== null) {
294
+ const parentName = match[1];
295
+ const subName = match[2];
296
+ if (parentName && subName) {
297
+ if (!subComponents.has(parentName)) {
298
+ subComponents.set(parentName, []);
299
+ }
300
+ subComponents.get(parentName)?.push(subName);
301
+ }
302
+ }
303
+ // Create results for each compound component
304
+ for (const [parentName, subs] of subComponents) {
305
+ const patternMatch = content.match(new RegExp(`${parentName}\\.${subs[0]}\\s*=`));
306
+ const line = patternMatch
307
+ ? content.slice(0, patternMatch.index).split('\n').length
308
+ : 1;
309
+ results.push({
310
+ pattern: 'compound-component',
311
+ componentName: parentName,
312
+ line,
313
+ column: 1,
314
+ details: {
315
+ subComponentNames: subs,
316
+ },
317
+ });
318
+ }
319
+ // Pattern 2: Usage of compound components <Parent.Child />
320
+ const usagePattern = /<([A-Z][a-zA-Z0-9]*)\.([A-Z][a-zA-Z0-9]*)/g;
321
+ const usedCompounds = new Set();
322
+ while ((match = usagePattern.exec(content)) !== null) {
323
+ const parentName = match[1];
324
+ if (parentName && !subComponents.has(parentName)) {
325
+ usedCompounds.add(parentName);
326
+ }
327
+ }
328
+ return results;
329
+ }
330
+ /**
331
+ * Check if component is part of a compound component pattern
332
+ */
333
+ export function isCompoundComponentPart(componentName, content) {
334
+ // Check if this component has sub-components attached
335
+ const hasSubComponents = new RegExp(`${componentName}\\.[A-Z][a-zA-Z0-9]*\\s*=`).test(content);
336
+ // Check if this component is used as Parent.Child
337
+ const isUsedAsCompound = new RegExp(`<${componentName}\\.[A-Z]`).test(content);
338
+ return hasSubComponents || isUsedAsCompound;
339
+ }
340
+ // ============================================================================
341
+ // Helper Functions - Slot-Based Composition Detection
342
+ // ============================================================================
343
+ /**
344
+ * Detect slot-based composition pattern
345
+ */
346
+ export function detectSlotBasedComposition(nodeText) {
347
+ const slotNames = [];
348
+ const slotKeywords = ['header', 'footer', 'sidebar', 'content', 'left', 'right', 'top', 'bottom', 'prefix', 'suffix', 'icon', 'label', 'title', 'description', 'actions', 'extra'];
349
+ // Pattern 1: Named slot props in destructuring ({ header, footer, sidebar })
350
+ const destructuringMatch = nodeText.match(/\(\s*\{\s*([^}]+)\s*\}/);
351
+ if (destructuringMatch && destructuringMatch[1]) {
352
+ const propsStr = destructuringMatch[1];
353
+ const props = propsStr.split(',').map(p => p.trim().split(/[=:]/)[0]?.trim() || '');
354
+ for (const prop of props) {
355
+ const propLower = prop.toLowerCase();
356
+ if (slotKeywords.includes(propLower) && !slotNames.includes(propLower)) {
357
+ slotNames.push(propLower);
358
+ }
359
+ }
360
+ }
361
+ // Pattern 2: Named slot props in JSX (header={...}, footer={...})
362
+ for (const keyword of slotKeywords) {
363
+ const pattern = new RegExp(`\\b${keyword}\\s*[=:]`, 'gi');
364
+ if (pattern.test(nodeText) && !slotNames.includes(keyword)) {
365
+ slotNames.push(keyword);
366
+ }
367
+ }
368
+ // Pattern 3: Render slot props (renderHeader, renderFooter, etc.)
369
+ const renderSlotPattern = /\b(render[A-Z][a-zA-Z]*)\s*[=:,)]/g;
370
+ let match;
371
+ while ((match = renderSlotPattern.exec(nodeText)) !== null) {
372
+ if (match[1]) {
373
+ const slotName = match[1].replace(/^render/, '').toLowerCase();
374
+ if (!slotNames.includes(slotName)) {
375
+ slotNames.push(slotName);
376
+ }
377
+ }
378
+ }
379
+ // Pattern 4: Render slot props in destructuring ({ renderHeader, renderFooter })
380
+ if (destructuringMatch && destructuringMatch[1]) {
381
+ const propsStr = destructuringMatch[1];
382
+ const renderPropMatches = propsStr.matchAll(/\b(render[A-Z][a-zA-Z]*)\b/g);
383
+ for (const renderMatch of renderPropMatches) {
384
+ if (renderMatch[1]) {
385
+ const slotName = renderMatch[1].replace(/^render/, '').toLowerCase();
386
+ if (!slotNames.includes(slotName)) {
387
+ slotNames.push(slotName);
388
+ }
389
+ }
390
+ }
391
+ }
392
+ // Pattern 5: Slot component usage (<Slot name="..." />)
393
+ const slotComponentPattern = /<Slot\s+name\s*=\s*["']([^"']+)["']/g;
394
+ while ((match = slotComponentPattern.exec(nodeText)) !== null) {
395
+ if (match[1] && !slotNames.includes(match[1])) {
396
+ slotNames.push(match[1]);
397
+ }
398
+ }
399
+ if (slotNames.length >= 2) {
400
+ return {
401
+ pattern: 'slot-based',
402
+ componentName: '',
403
+ line: 1,
404
+ column: 1,
405
+ details: {
406
+ slotNames,
407
+ },
408
+ };
409
+ }
410
+ return null;
411
+ }
412
+ // ============================================================================
413
+ // Helper Functions - Provider/Consumer Detection
414
+ // ============================================================================
415
+ /**
416
+ * Detect Provider/Consumer pattern
417
+ */
418
+ export function detectProviderConsumer(content) {
419
+ const results = [];
420
+ // Pattern 1: Context.Provider usage
421
+ const providerPattern = /<([A-Z][a-zA-Z0-9]*(?:Context)?)\s*\.\s*Provider/g;
422
+ let match;
423
+ while ((match = providerPattern.exec(content)) !== null) {
424
+ const contextName = match[1];
425
+ if (!contextName)
426
+ continue;
427
+ const beforeMatch = content.slice(0, match.index);
428
+ const line = beforeMatch.split('\n').length;
429
+ results.push({
430
+ pattern: 'provider-consumer',
431
+ componentName: '',
432
+ line,
433
+ column: 1,
434
+ details: {
435
+ contextName,
436
+ },
437
+ });
438
+ }
439
+ // Pattern 2: Context.Consumer usage
440
+ const consumerPattern = /<([A-Z][a-zA-Z0-9]*(?:Context)?)\s*\.\s*Consumer/g;
441
+ while ((match = consumerPattern.exec(content)) !== null) {
442
+ const contextName = match[1];
443
+ if (!contextName)
444
+ continue;
445
+ const beforeMatch = content.slice(0, match.index);
446
+ const line = beforeMatch.split('\n').length;
447
+ // Check if we already have this context
448
+ const existing = results.find(r => r.details.contextName === contextName);
449
+ if (!existing) {
450
+ results.push({
451
+ pattern: 'provider-consumer',
452
+ componentName: '',
453
+ line,
454
+ column: 1,
455
+ details: {
456
+ contextName,
457
+ },
458
+ });
459
+ }
460
+ }
461
+ // Pattern 3: createContext usage
462
+ const createContextPattern = /const\s+([A-Z][a-zA-Z0-9]*(?:Context)?)\s*=\s*(?:React\.)?createContext/g;
463
+ while ((match = createContextPattern.exec(content)) !== null) {
464
+ const contextName = match[1];
465
+ if (!contextName)
466
+ continue;
467
+ const beforeMatch = content.slice(0, match.index);
468
+ const line = beforeMatch.split('\n').length;
469
+ results.push({
470
+ pattern: 'provider-consumer',
471
+ componentName: '',
472
+ line,
473
+ column: 1,
474
+ details: {
475
+ contextName,
476
+ },
477
+ });
478
+ }
479
+ return results;
480
+ }
481
+ /**
482
+ * Count nested providers in content
483
+ */
484
+ export function countNestedProviders(content) {
485
+ const providerMatches = content.match(/<[A-Z][a-zA-Z0-9]*(?:Context)?\s*\.\s*Provider/g);
486
+ return providerMatches ? providerMatches.length : 0;
487
+ }
488
+ // ============================================================================
489
+ // Helper Functions - Controlled/Uncontrolled Detection
490
+ // ============================================================================
491
+ /**
492
+ * Detect controlled component pattern
493
+ */
494
+ export function detectControlledPattern(nodeText) {
495
+ const controlledProps = [];
496
+ for (const pattern of CONTROLLED_PROP_PATTERNS) {
497
+ const valuePattern = new RegExp(`\\b${pattern.value}\\s*[=:]`, 'g');
498
+ const onChangePattern = new RegExp(`\\b${pattern.onChange}\\s*[=:]`, 'g');
499
+ if (valuePattern.test(nodeText) && onChangePattern.test(nodeText)) {
500
+ controlledProps.push(pattern.value);
501
+ }
502
+ }
503
+ if (controlledProps.length > 0) {
504
+ return {
505
+ pattern: 'controlled',
506
+ componentName: '',
507
+ line: 1,
508
+ column: 1,
509
+ details: {
510
+ controlledProps,
511
+ },
512
+ };
513
+ }
514
+ return null;
515
+ }
516
+ /**
517
+ * Detect uncontrolled component pattern
518
+ */
519
+ export function detectUncontrolledPattern(nodeText) {
520
+ const uncontrolledProps = [];
521
+ for (const propName of UNCONTROLLED_PROP_PATTERNS) {
522
+ const pattern = new RegExp(`\\b${propName}\\s*[=:]`, 'g');
523
+ if (pattern.test(nodeText)) {
524
+ uncontrolledProps.push(propName);
525
+ }
526
+ }
527
+ // Also check for ref usage without value prop (uncontrolled form elements)
528
+ const hasRef = /\bref\s*[=:]/.test(nodeText);
529
+ const hasValue = /\bvalue\s*[=:]/.test(nodeText);
530
+ if (hasRef && !hasValue) {
531
+ uncontrolledProps.push('ref');
532
+ }
533
+ if (uncontrolledProps.length > 0) {
534
+ return {
535
+ pattern: 'uncontrolled',
536
+ componentName: '',
537
+ line: 1,
538
+ column: 1,
539
+ details: {
540
+ controlledProps: uncontrolledProps,
541
+ },
542
+ };
543
+ }
544
+ return null;
545
+ }
546
+ /**
547
+ * Check if component mixes controlled and uncontrolled patterns
548
+ */
549
+ export function hasMixedControlledPatterns(nodeText) {
550
+ const hasControlled = detectControlledPattern(nodeText) !== null;
551
+ const hasUncontrolled = detectUncontrolledPattern(nodeText) !== null;
552
+ return hasControlled && hasUncontrolled;
553
+ }
554
+ // ============================================================================
555
+ // Helper Functions - Anti-Pattern Detection
556
+ // ============================================================================
557
+ /**
558
+ * Detect composition anti-patterns
559
+ */
560
+ export function detectAntiPatterns(nodeText, content, config) {
561
+ const antiPatterns = [];
562
+ // Check for deeply nested HOCs
563
+ const hocUsages = detectHOC(content);
564
+ for (const hoc of hocUsages) {
565
+ if (hoc.details.nestingDepth && hoc.details.nestingDepth > config.maxHOCDepth) {
566
+ antiPatterns.push({
567
+ type: 'deeply-nested-hocs',
568
+ description: `Component has ${hoc.details.nestingDepth} nested HOCs (max: ${config.maxHOCDepth})`,
569
+ severity: 'warning',
570
+ suggestion: 'Consider using hooks or composition instead of deeply nested HOCs',
571
+ line: hoc.line,
572
+ column: 1,
573
+ });
574
+ }
575
+ }
576
+ // Check for overuse of render props
577
+ const renderProps = detectRenderProps(nodeText);
578
+ if (renderProps && renderProps.details.renderPropNames) {
579
+ const count = renderProps.details.renderPropNames.length;
580
+ if (count > config.maxRenderProps) {
581
+ antiPatterns.push({
582
+ type: 'overuse-render-props',
583
+ description: `Component has ${count} render props (max: ${config.maxRenderProps})`,
584
+ severity: 'info',
585
+ suggestion: 'Consider splitting into smaller components or using composition',
586
+ line: renderProps.line,
587
+ column: 1,
588
+ });
589
+ }
590
+ }
591
+ // Check for excessive context nesting
592
+ const providerCount = countNestedProviders(content);
593
+ if (providerCount > config.maxContextNesting) {
594
+ antiPatterns.push({
595
+ type: 'excessive-context',
596
+ description: `File has ${providerCount} nested context providers (max: ${config.maxContextNesting})`,
597
+ severity: 'warning',
598
+ suggestion: 'Consider combining related contexts or using a state management library',
599
+ line: 1,
600
+ column: 1,
601
+ });
602
+ }
603
+ // Check for mixed controlled/uncontrolled patterns
604
+ if (config.detectControlledPatterns && hasMixedControlledPatterns(nodeText)) {
605
+ antiPatterns.push({
606
+ type: 'mixed-controlled',
607
+ description: 'Component mixes controlled and uncontrolled patterns',
608
+ severity: 'warning',
609
+ suggestion: 'Use either controlled or uncontrolled pattern consistently',
610
+ line: 1,
611
+ column: 1,
612
+ });
613
+ }
614
+ // Check for missing children prop when component renders children
615
+ const rendersChildElements = /<[A-Z][a-zA-Z0-9]*[^>]*>[^<]+<\/[A-Z]/.test(nodeText);
616
+ const acceptsChildren = acceptsChildrenProp(nodeText);
617
+ if (rendersChildElements && !acceptsChildren) {
618
+ // This might indicate missing children prop
619
+ // Only flag if component seems to be a wrapper
620
+ const isWrapper = /return\s*\(\s*<[^>]+>\s*\{/.test(nodeText);
621
+ if (isWrapper) {
622
+ antiPatterns.push({
623
+ type: 'missing-children',
624
+ description: 'Component appears to be a wrapper but does not accept children prop',
625
+ severity: 'info',
626
+ suggestion: 'Consider adding children prop to allow composition',
627
+ line: 1,
628
+ column: 1,
629
+ });
630
+ }
631
+ }
632
+ return antiPatterns;
633
+ }
634
+ // ============================================================================
635
+ // Helper Functions - Analysis
636
+ // ============================================================================
637
+ /**
638
+ * Analyze a single component's composition patterns
639
+ */
640
+ export function analyzeComponentComposition(node, content, filePath, config) {
641
+ const componentName = getComponentName(node, content);
642
+ if (!componentName) {
643
+ return null;
644
+ }
645
+ const nodeText = node.text;
646
+ const patterns = [];
647
+ // Detect children prop usage
648
+ const childrenUsage = detectChildrenProp(nodeText);
649
+ if (childrenUsage) {
650
+ childrenUsage.componentName = componentName;
651
+ patterns.push(childrenUsage);
652
+ }
653
+ // Detect render props
654
+ const renderPropsUsage = detectRenderProps(nodeText);
655
+ if (renderPropsUsage) {
656
+ renderPropsUsage.componentName = componentName;
657
+ patterns.push(renderPropsUsage);
658
+ }
659
+ // Detect slot-based composition
660
+ const slotUsage = detectSlotBasedComposition(nodeText);
661
+ if (slotUsage) {
662
+ slotUsage.componentName = componentName;
663
+ patterns.push(slotUsage);
664
+ }
665
+ // Detect controlled pattern
666
+ if (config.detectControlledPatterns) {
667
+ const controlledUsage = detectControlledPattern(nodeText);
668
+ if (controlledUsage) {
669
+ controlledUsage.componentName = componentName;
670
+ patterns.push(controlledUsage);
671
+ }
672
+ const uncontrolledUsage = detectUncontrolledPattern(nodeText);
673
+ if (uncontrolledUsage) {
674
+ uncontrolledUsage.componentName = componentName;
675
+ patterns.push(uncontrolledUsage);
676
+ }
677
+ }
678
+ // Detect HOCs (from full content, not just node)
679
+ const hocUsages = detectHOC(content);
680
+ for (const hoc of hocUsages) {
681
+ if (hoc.componentName === componentName || hoc.componentName === '') {
682
+ hoc.componentName = componentName;
683
+ patterns.push(hoc);
684
+ }
685
+ }
686
+ // Detect compound components
687
+ const compoundUsages = detectCompoundComponent(content);
688
+ for (const compound of compoundUsages) {
689
+ if (compound.componentName === componentName) {
690
+ patterns.push(compound);
691
+ }
692
+ }
693
+ // Detect provider/consumer
694
+ const providerUsages = detectProviderConsumer(nodeText);
695
+ for (const provider of providerUsages) {
696
+ provider.componentName = componentName;
697
+ patterns.push(provider);
698
+ }
699
+ // Detect anti-patterns
700
+ const antiPatterns = detectAntiPatterns(nodeText, content, config);
701
+ return {
702
+ componentName,
703
+ filePath,
704
+ line: node.startPosition.row + 1,
705
+ column: node.startPosition.column + 1,
706
+ patterns,
707
+ antiPatterns,
708
+ acceptsChildren: acceptsChildrenProp(nodeText),
709
+ usesRenderProps: renderPropsUsage !== null,
710
+ isHOC: hocUsages.some(h => h.componentName === componentName),
711
+ isCompoundComponent: isCompoundComponentPart(componentName, content),
712
+ isControlled: detectControlledPattern(nodeText) !== null,
713
+ };
714
+ }
715
+ /**
716
+ * Find dominant pattern from a list
717
+ */
718
+ function findDominantPattern(patterns) {
719
+ const counts = new Map();
720
+ for (const pattern of patterns) {
721
+ if (pattern !== 'none') {
722
+ counts.set(pattern, (counts.get(pattern) || 0) + 1);
723
+ }
724
+ }
725
+ let dominant = 'none';
726
+ let maxCount = 0;
727
+ for (const [pattern, count] of counts) {
728
+ if (count > maxCount) {
729
+ maxCount = count;
730
+ dominant = pattern;
731
+ }
732
+ }
733
+ const total = patterns.filter(p => p !== 'none').length;
734
+ const confidence = total > 0 ? maxCount / total : 0;
735
+ return { dominant, confidence };
736
+ }
737
+ /**
738
+ * Analyze composition patterns across multiple components
739
+ */
740
+ export function analyzeCompositionPatterns(components) {
741
+ if (components.length === 0) {
742
+ return {
743
+ components: [],
744
+ dominantPattern: 'none',
745
+ patternCounts: {
746
+ 'children-prop': 0,
747
+ 'render-props': 0,
748
+ 'hoc': 0,
749
+ 'compound-component': 0,
750
+ 'slot-based': 0,
751
+ 'provider-consumer': 0,
752
+ 'controlled': 0,
753
+ 'uncontrolled': 0,
754
+ 'forwarded-ref': 0,
755
+ 'none': 0,
756
+ },
757
+ confidence: 0,
758
+ componentsWithAntiPatterns: [],
759
+ healthScore: 1.0,
760
+ };
761
+ }
762
+ // Collect all patterns
763
+ const allPatterns = [];
764
+ const patternCounts = {
765
+ 'children-prop': 0,
766
+ 'render-props': 0,
767
+ 'hoc': 0,
768
+ 'compound-component': 0,
769
+ 'slot-based': 0,
770
+ 'provider-consumer': 0,
771
+ 'controlled': 0,
772
+ 'uncontrolled': 0,
773
+ 'forwarded-ref': 0,
774
+ 'none': 0,
775
+ };
776
+ for (const comp of components) {
777
+ for (const pattern of comp.patterns) {
778
+ allPatterns.push(pattern.pattern);
779
+ patternCounts[pattern.pattern]++;
780
+ }
781
+ if (comp.patterns.length === 0) {
782
+ patternCounts['none']++;
783
+ }
784
+ }
785
+ // Find dominant pattern
786
+ const { dominant, confidence } = findDominantPattern(allPatterns);
787
+ // Find components with anti-patterns
788
+ const componentsWithAntiPatterns = components.filter(c => c.antiPatterns.length > 0);
789
+ // Calculate health score
790
+ const totalAntiPatterns = components.reduce((sum, c) => sum + c.antiPatterns.length, 0);
791
+ const healthScore = Math.max(0, 1 - (totalAntiPatterns / (components.length * 2)));
792
+ return {
793
+ components,
794
+ dominantPattern: dominant,
795
+ patternCounts,
796
+ confidence,
797
+ componentsWithAntiPatterns,
798
+ healthScore,
799
+ };
800
+ }
801
+ // ============================================================================
802
+ // Pattern Description Helpers
803
+ // ============================================================================
804
+ const ANTI_PATTERN_DESCRIPTIONS = {
805
+ 'deeply-nested-hocs': 'deeply nested HOCs',
806
+ 'prop-drilling': 'prop drilling',
807
+ 'missing-children': 'missing children prop',
808
+ 'overuse-render-props': 'overuse of render props',
809
+ 'mixed-controlled': 'mixed controlled/uncontrolled patterns',
810
+ 'excessive-context': 'excessive context nesting',
811
+ };
812
+ // ============================================================================
813
+ // Composition Detector Class
814
+ // ============================================================================
815
+ /**
816
+ * Detector for component composition patterns
817
+ *
818
+ * Identifies composition patterns including children prop usage, render props,
819
+ * HOCs, compound components, and more. Reports anti-patterns and suggests
820
+ * improvements.
821
+ *
822
+ * @requirements 8.6 - THE Component_Detector SHALL detect component composition patterns
823
+ */
824
+ export class CompositionDetector extends ASTDetector {
825
+ id = 'components/composition';
826
+ category = 'components';
827
+ subcategory = 'composition';
828
+ name = 'Composition Pattern Detector';
829
+ description = 'Detects component composition patterns (children, render props, HOCs, compound components) and identifies anti-patterns';
830
+ supportedLanguages = ['typescript', 'javascript'];
831
+ config;
832
+ constructor(config = {}) {
833
+ super();
834
+ this.config = { ...DEFAULT_COMPOSITION_CONFIG, ...config };
835
+ }
836
+ /**
837
+ * Detect composition patterns in the project
838
+ */
839
+ async detect(context) {
840
+ const patterns = [];
841
+ const violations = [];
842
+ // Find all components in the file
843
+ const componentInfos = this.findComponentsInFile(context);
844
+ if (componentInfos.length === 0) {
845
+ return this.createEmptyResult();
846
+ }
847
+ // Analyze patterns across all components
848
+ const analysis = analyzeCompositionPatterns(componentInfos);
849
+ // Create pattern matches for detected patterns
850
+ for (const [patternType, count] of Object.entries(analysis.patternCounts)) {
851
+ if (count > 0 && patternType !== 'none') {
852
+ patterns.push(this.createPatternMatch(context.file, patternType, count, analysis));
853
+ }
854
+ }
855
+ // Generate violations for anti-patterns
856
+ for (const comp of analysis.componentsWithAntiPatterns) {
857
+ if (comp.filePath === context.file) {
858
+ for (const antiPattern of comp.antiPatterns) {
859
+ violations.push(this.createAntiPatternViolation(context.file, comp, antiPattern));
860
+ }
861
+ }
862
+ }
863
+ return this.createResult(patterns, violations, analysis.confidence);
864
+ }
865
+ /**
866
+ * Find all React components in a file
867
+ */
868
+ findComponentsInFile(context) {
869
+ const components = [];
870
+ if (context.ast) {
871
+ // Use AST to find components
872
+ const functionNodes = this.findNodesByTypes(context.ast, [
873
+ 'function_declaration',
874
+ 'arrow_function',
875
+ 'function_expression',
876
+ ]);
877
+ for (const node of functionNodes) {
878
+ if (isReactComponent(node, context.content)) {
879
+ const info = analyzeComponentComposition(node, context.content, context.file, this.config);
880
+ if (info) {
881
+ components.push(info);
882
+ }
883
+ }
884
+ }
885
+ }
886
+ else {
887
+ // Fallback: use regex-based detection
888
+ const componentMatches = this.findComponentsWithRegex(context.content, context.file);
889
+ components.push(...componentMatches);
890
+ }
891
+ return components;
892
+ }
893
+ /**
894
+ * Find components using regex (fallback when AST is not available)
895
+ */
896
+ findComponentsWithRegex(content, filePath) {
897
+ const components = [];
898
+ // Pattern for arrow function components
899
+ const arrowPattern = /(?:export\s+)?const\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*(?:React\.)?(?:FC|FunctionComponent)\s*<[^>]*>\s*)?=\s*\(/g;
900
+ // Pattern for function declaration components
901
+ const functionPattern = /(?:export\s+)?function\s+([A-Z][a-zA-Z0-9]*)\s*\(/g;
902
+ const processMatch = (match, componentName) => {
903
+ const beforeMatch = content.slice(0, match.index);
904
+ const lineNumber = beforeMatch.split('\n').length;
905
+ // Get component body (simplified)
906
+ const startIndex = match.index;
907
+ let braceCount = 0;
908
+ let endIndex = startIndex;
909
+ let foundStart = false;
910
+ for (let i = startIndex; i < content.length && i < startIndex + 10000; i++) {
911
+ const char = content[i];
912
+ if (char === '{') {
913
+ braceCount++;
914
+ foundStart = true;
915
+ }
916
+ else if (char === '}') {
917
+ braceCount--;
918
+ if (foundStart && braceCount === 0) {
919
+ endIndex = i + 1;
920
+ break;
921
+ }
922
+ }
923
+ }
924
+ const componentBody = content.slice(startIndex, endIndex);
925
+ // Check if it's a React component (contains JSX)
926
+ if (!componentBody.includes('<') || (!componentBody.includes('/>') && !componentBody.includes('</'))) {
927
+ return;
928
+ }
929
+ const patterns = [];
930
+ // Detect patterns
931
+ const childrenUsage = detectChildrenProp(componentBody);
932
+ if (childrenUsage) {
933
+ childrenUsage.componentName = componentName;
934
+ patterns.push(childrenUsage);
935
+ }
936
+ const renderPropsUsage = detectRenderProps(componentBody);
937
+ if (renderPropsUsage) {
938
+ renderPropsUsage.componentName = componentName;
939
+ patterns.push(renderPropsUsage);
940
+ }
941
+ const slotUsage = detectSlotBasedComposition(componentBody);
942
+ if (slotUsage) {
943
+ slotUsage.componentName = componentName;
944
+ patterns.push(slotUsage);
945
+ }
946
+ if (this.config.detectControlledPatterns) {
947
+ const controlledUsage = detectControlledPattern(componentBody);
948
+ if (controlledUsage) {
949
+ controlledUsage.componentName = componentName;
950
+ patterns.push(controlledUsage);
951
+ }
952
+ const uncontrolledUsage = detectUncontrolledPattern(componentBody);
953
+ if (uncontrolledUsage) {
954
+ uncontrolledUsage.componentName = componentName;
955
+ patterns.push(uncontrolledUsage);
956
+ }
957
+ }
958
+ // Detect anti-patterns
959
+ const antiPatterns = detectAntiPatterns(componentBody, content, this.config);
960
+ components.push({
961
+ componentName,
962
+ filePath,
963
+ line: lineNumber,
964
+ column: 1,
965
+ patterns,
966
+ antiPatterns,
967
+ acceptsChildren: acceptsChildrenProp(componentBody),
968
+ usesRenderProps: renderPropsUsage !== null,
969
+ isHOC: false,
970
+ isCompoundComponent: isCompoundComponentPart(componentName, content),
971
+ isControlled: detectControlledPattern(componentBody) !== null,
972
+ });
973
+ };
974
+ let match;
975
+ while ((match = arrowPattern.exec(content)) !== null) {
976
+ if (match[1]) {
977
+ processMatch(match, match[1]);
978
+ }
979
+ }
980
+ while ((match = functionPattern.exec(content)) !== null) {
981
+ if (match[1]) {
982
+ processMatch(match, match[1]);
983
+ }
984
+ }
985
+ // Also detect HOCs and compound components at file level
986
+ const hocUsages = detectHOC(content);
987
+ for (const hoc of hocUsages) {
988
+ const existing = components.find(c => c.componentName === hoc.componentName);
989
+ if (existing) {
990
+ existing.patterns.push(hoc);
991
+ existing.isHOC = true;
992
+ }
993
+ }
994
+ const compoundUsages = detectCompoundComponent(content);
995
+ for (const compound of compoundUsages) {
996
+ const existing = components.find(c => c.componentName === compound.componentName);
997
+ if (existing) {
998
+ existing.patterns.push(compound);
999
+ existing.isCompoundComponent = true;
1000
+ }
1001
+ }
1002
+ const providerUsages = detectProviderConsumer(content);
1003
+ for (const provider of providerUsages) {
1004
+ // Add to first component or create a placeholder
1005
+ if (components.length > 0 && components[0]) {
1006
+ components[0].patterns.push(provider);
1007
+ }
1008
+ }
1009
+ return components;
1010
+ }
1011
+ /**
1012
+ * Create a pattern match for a composition pattern
1013
+ */
1014
+ createPatternMatch(file, patternType, count, analysis) {
1015
+ const total = Object.values(analysis.patternCounts).reduce((a, b) => a + b, 0) - analysis.patternCounts['none'];
1016
+ const confidence = total > 0 ? count / total : 0;
1017
+ return {
1018
+ patternId: `composition-${patternType}`,
1019
+ location: { file, line: 1, column: 1 },
1020
+ confidence,
1021
+ isOutlier: confidence < 0.2,
1022
+ };
1023
+ }
1024
+ /**
1025
+ * Create a violation for an anti-pattern
1026
+ */
1027
+ createAntiPatternViolation(file, component, antiPattern) {
1028
+ const range = {
1029
+ start: { line: antiPattern.line, character: antiPattern.column },
1030
+ end: { line: antiPattern.line, character: antiPattern.column + 1 },
1031
+ };
1032
+ return {
1033
+ id: `composition-${component.componentName}-${antiPattern.type}-${file.replace(/[^a-zA-Z0-9]/g, '-')}`,
1034
+ patternId: 'components/composition',
1035
+ severity: antiPattern.severity,
1036
+ file,
1037
+ range,
1038
+ message: `${component.componentName}: ${antiPattern.description}`,
1039
+ expected: 'Clean composition pattern',
1040
+ actual: ANTI_PATTERN_DESCRIPTIONS[antiPattern.type],
1041
+ explanation: antiPattern.suggestion,
1042
+ aiExplainAvailable: true,
1043
+ aiFixAvailable: antiPattern.type !== 'deeply-nested-hocs',
1044
+ firstSeen: new Date(),
1045
+ occurrences: 1,
1046
+ };
1047
+ }
1048
+ /**
1049
+ * Generate a quick fix for composition violations
1050
+ */
1051
+ generateQuickFix(violation) {
1052
+ // Extract anti-pattern type from violation
1053
+ // ID format: composition-ComponentName-antiPatternType-filePath
1054
+ // antiPatternType can contain hyphens (e.g., missing-children, mixed-controlled)
1055
+ const antiPatternMatch = violation.id.match(/composition-[A-Za-z0-9]+-([a-z]+-[a-z]+|[a-z]+)-/);
1056
+ if (!antiPatternMatch || !antiPatternMatch[1]) {
1057
+ return null;
1058
+ }
1059
+ const antiPatternType = antiPatternMatch[1];
1060
+ switch (antiPatternType) {
1061
+ case 'missing-children':
1062
+ return {
1063
+ title: 'Add children prop',
1064
+ kind: 'quickfix',
1065
+ edit: {
1066
+ changes: {},
1067
+ documentChanges: [],
1068
+ },
1069
+ isPreferred: true,
1070
+ confidence: 0.8,
1071
+ preview: 'Add children: React.ReactNode to props type',
1072
+ };
1073
+ case 'mixed-controlled':
1074
+ return {
1075
+ title: 'Convert to controlled component',
1076
+ kind: 'refactor',
1077
+ edit: {
1078
+ changes: {},
1079
+ documentChanges: [],
1080
+ },
1081
+ isPreferred: true,
1082
+ confidence: 0.7,
1083
+ preview: 'Remove default* props and use value/onChange pattern',
1084
+ };
1085
+ case 'overuse-render-props':
1086
+ return {
1087
+ title: 'Extract render props to separate components',
1088
+ kind: 'refactor',
1089
+ edit: {
1090
+ changes: {},
1091
+ documentChanges: [],
1092
+ },
1093
+ isPreferred: false,
1094
+ confidence: 0.6,
1095
+ preview: 'Split component into smaller, focused components',
1096
+ };
1097
+ case 'excessive-context':
1098
+ return {
1099
+ title: 'Combine related contexts',
1100
+ kind: 'refactor',
1101
+ edit: {
1102
+ changes: {},
1103
+ documentChanges: [],
1104
+ },
1105
+ isPreferred: false,
1106
+ confidence: 0.5,
1107
+ preview: 'Merge related context providers into a single provider',
1108
+ };
1109
+ default:
1110
+ return null;
1111
+ }
1112
+ }
1113
+ }
1114
+ // ============================================================================
1115
+ // Factory Function
1116
+ // ============================================================================
1117
+ /**
1118
+ * Create a new CompositionDetector instance
1119
+ */
1120
+ export function createCompositionDetector(config = {}) {
1121
+ return new CompositionDetector(config);
1122
+ }
1123
+ //# sourceMappingURL=composition.js.map