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,1181 @@
1
+ /**
2
+ * Tailwind Patterns Detector - Tailwind CSS pattern consistency detection
3
+ *
4
+ * Detects Tailwind CSS patterns including:
5
+ * - Standard Tailwind utility classes
6
+ * - Tailwind arbitrary values (e.g., p-[13px], text-[#ff0000])
7
+ * - Tailwind responsive prefixes (sm:, md:, lg:, xl:, 2xl:)
8
+ * - Tailwind state variants (hover:, focus:, active:, disabled:)
9
+ * - Tailwind dark mode (dark:)
10
+ * - @apply directive usage
11
+ * - Tailwind config customizations
12
+ *
13
+ * Flags inconsistent Tailwind usage:
14
+ * - Mixing arbitrary values with standard classes
15
+ * - Inconsistent responsive breakpoint ordering
16
+ * - Redundant/conflicting classes (e.g., flex and block on same element)
17
+ * - Non-standard class ordering
18
+ *
19
+ * @requirements 9.6 - THE Styling_Detector SHALL detect Tailwind pattern consistency
20
+ */
21
+ import { RegexDetector } from '../base/index.js';
22
+ // ============================================================================
23
+ // Constants
24
+ // ============================================================================
25
+ /**
26
+ * Standard Tailwind utility class patterns
27
+ */
28
+ export const TAILWIND_UTILITY_PATTERNS = [
29
+ // Layout
30
+ /\b(?:flex|grid|block|inline|inline-block|inline-flex|inline-grid|hidden|contents|flow-root)\b/g,
31
+ /\b(?:flex-row|flex-col|flex-row-reverse|flex-col-reverse|flex-wrap|flex-nowrap|flex-wrap-reverse)\b/g,
32
+ /\b(?:flex-1|flex-auto|flex-initial|flex-none)\b/g,
33
+ /\b(?:grid-cols-\d+|grid-rows-\d+|col-span-\d+|row-span-\d+|col-start-\d+|col-end-\d+|row-start-\d+|row-end-\d+)\b/g,
34
+ // Spacing
35
+ /\b(?:p|px|py|pt|pr|pb|pl)-(?:\d+(?:\.\d+)?|auto|px)\b/g,
36
+ /\b(?:m|mx|my|mt|mr|mb|ml)-(?:\d+(?:\.\d+)?|auto|px)\b/g,
37
+ /\b(?:space-x|space-y)-(?:\d+(?:\.\d+)?|reverse)\b/g,
38
+ /\b(?:gap|gap-x|gap-y)-\d+(?:\.\d+)?\b/g,
39
+ // Sizing
40
+ /\b(?:w|h|min-w|min-h|max-w|max-h|size)-(?:\d+(?:\/\d+)?|full|screen|auto|min|max|fit|px)\b/g,
41
+ // Typography
42
+ /\b(?:text-(?:xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl))\b/g,
43
+ /\b(?:font-(?:thin|extralight|light|normal|medium|semibold|bold|extrabold|black))\b/g,
44
+ /\b(?:leading-(?:none|tight|snug|normal|relaxed|loose|\d+))\b/g,
45
+ /\b(?:tracking-(?:tighter|tight|normal|wide|wider|widest))\b/g,
46
+ // Colors
47
+ /\b(?:text|bg|border|ring|divide|outline|accent|caret|fill|stroke)-(?:inherit|current|transparent|black|white)\b/g,
48
+ /\b(?:text|bg|border|ring|divide|outline|accent|caret|fill|stroke)-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:\d{2,3}|50)\b/g,
49
+ // Flexbox alignment
50
+ /\b(?:items|justify|content|self|place-content|place-items|place-self)-(?:start|end|center|between|around|evenly|stretch|baseline|auto)\b/g,
51
+ // Border
52
+ /\b(?:border|border-t|border-r|border-b|border-l|border-x|border-y)(?:-\d+)?\b/g,
53
+ /\b(?:rounded|rounded-t|rounded-r|rounded-b|rounded-l|rounded-tl|rounded-tr|rounded-bl|rounded-br|rounded-s|rounded-e|rounded-ss|rounded-se|rounded-es|rounded-ee)(?:-(?:none|sm|md|lg|xl|2xl|3xl|full))?\b/g,
54
+ // Effects
55
+ /\b(?:shadow|shadow-sm|shadow-md|shadow-lg|shadow-xl|shadow-2xl|shadow-inner|shadow-none)\b/g,
56
+ /\b(?:opacity-\d+)\b/g,
57
+ // Positioning
58
+ /\b(?:relative|absolute|fixed|sticky|static)\b/g,
59
+ /\b(?:top|right|bottom|left|inset|inset-x|inset-y)-(?:\d+(?:\/\d+)?|auto|px|full)\b/g,
60
+ /\b(?:z-\d+|z-auto)\b/g,
61
+ // Display
62
+ /\b(?:overflow|overflow-x|overflow-y)-(?:auto|hidden|visible|scroll|clip)\b/g,
63
+ // Transitions
64
+ /\b(?:transition|transition-all|transition-colors|transition-opacity|transition-shadow|transition-transform|transition-none)\b/g,
65
+ /\b(?:duration-\d+)\b/g,
66
+ /\b(?:ease-linear|ease-in|ease-out|ease-in-out)\b/g,
67
+ /\b(?:delay-\d+)\b/g,
68
+ // Transforms
69
+ /\b(?:scale|scale-x|scale-y)-\d+\b/g,
70
+ /\b(?:rotate-\d+)\b/g,
71
+ /\b(?:translate-x|translate-y)-(?:\d+(?:\/\d+)?|full|px)\b/g,
72
+ /\b(?:skew-x|skew-y)-\d+\b/g,
73
+ // Cursor
74
+ /\b(?:cursor-(?:auto|default|pointer|wait|text|move|help|not-allowed|none|context-menu|progress|cell|crosshair|vertical-text|alias|copy|no-drop|grab|grabbing|all-scroll|col-resize|row-resize|n-resize|e-resize|s-resize|w-resize|ne-resize|nw-resize|se-resize|sw-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|zoom-in|zoom-out))\b/g,
75
+ // Pointer events
76
+ /\b(?:pointer-events-(?:none|auto))\b/g,
77
+ // User select
78
+ /\b(?:select-(?:none|text|all|auto))\b/g,
79
+ // Visibility
80
+ /\b(?:visible|invisible|collapse)\b/g,
81
+ // Whitespace
82
+ /\b(?:whitespace-(?:normal|nowrap|pre|pre-line|pre-wrap|break-spaces))\b/g,
83
+ // Word break
84
+ /\b(?:break-(?:normal|words|all|keep))\b/g,
85
+ // Text alignment
86
+ /\b(?:text-(?:left|center|right|justify|start|end))\b/g,
87
+ // Text decoration
88
+ /\b(?:underline|overline|line-through|no-underline)\b/g,
89
+ // Text transform
90
+ /\b(?:uppercase|lowercase|capitalize|normal-case)\b/g,
91
+ // Aspect ratio
92
+ /\b(?:aspect-(?:auto|square|video))\b/g,
93
+ // Object fit
94
+ /\b(?:object-(?:contain|cover|fill|none|scale-down))\b/g,
95
+ ];
96
+ /**
97
+ * Tailwind arbitrary value patterns
98
+ * Matches: p-[13px], text-[#ff0000], w-[calc(100%-20px)], etc.
99
+ */
100
+ export const TAILWIND_ARBITRARY_VALUE_PATTERN = /\b([a-z][a-z0-9-]*)-\[([^\]]+)\]/gi;
101
+ /**
102
+ * Tailwind responsive prefix patterns
103
+ * Matches: sm:, md:, lg:, xl:, 2xl:
104
+ */
105
+ export const TAILWIND_RESPONSIVE_PREFIXES = ['sm', 'md', 'lg', 'xl', '2xl'];
106
+ export const TAILWIND_RESPONSIVE_PATTERN = /\b(sm|md|lg|xl|2xl):([a-z][a-z0-9-]*(?:-[a-z0-9]+)*(?:\[[^\]]+\])?)/gi;
107
+ /**
108
+ * Tailwind state variant patterns
109
+ * Matches: hover:, focus:, active:, disabled:, etc.
110
+ */
111
+ export const TAILWIND_STATE_VARIANTS = [
112
+ 'hover', 'focus', 'active', 'disabled', 'visited', 'focus-within', 'focus-visible',
113
+ 'first', 'last', 'odd', 'even', 'first-of-type', 'last-of-type', 'only-child', 'only-of-type',
114
+ 'empty', 'enabled', 'checked', 'indeterminate', 'default', 'required', 'valid', 'invalid',
115
+ 'in-range', 'out-of-range', 'placeholder-shown', 'autofill', 'read-only',
116
+ 'before', 'after', 'first-letter', 'first-line', 'marker', 'selection', 'file', 'backdrop', 'placeholder',
117
+ 'group-hover', 'group-focus', 'peer-hover', 'peer-focus', 'peer-checked',
118
+ ];
119
+ export const TAILWIND_STATE_VARIANT_PATTERN = new RegExp(`\\b(${TAILWIND_STATE_VARIANTS.join('|')}):([a-z][a-z0-9-]*(?:-[a-z0-9]+)*(?:\\[[^\\]]+\\])?)`, 'gi');
120
+ /**
121
+ * Tailwind dark mode pattern
122
+ * Matches: dark:bg-gray-800, dark:text-white, etc.
123
+ */
124
+ export const TAILWIND_DARK_MODE_PATTERN = /\bdark:([a-z][a-z0-9-]*(?:-[a-z0-9]+)*(?:\[[^\]]+\])?)/gi;
125
+ /**
126
+ * @apply directive pattern
127
+ * Matches: @apply flex items-center p-4;
128
+ */
129
+ export const TAILWIND_APPLY_PATTERN = /@apply\s+([^;]+);?/gi;
130
+ /**
131
+ * Tailwind config customization patterns
132
+ */
133
+ export const TAILWIND_CONFIG_PATTERNS = [
134
+ /theme:\s*\{/g,
135
+ /extend:\s*\{/g,
136
+ /plugins:\s*\[/g,
137
+ /content:\s*\[/g,
138
+ ];
139
+ /**
140
+ * Conflicting class pairs - classes that shouldn't be used together
141
+ */
142
+ export const CONFLICTING_CLASS_PAIRS = [
143
+ [/\bflex\b/, /\bgrid\b/, 'flex and grid'],
144
+ [/\bflex\b/, /\bblock\b/, 'flex and block'],
145
+ [/\bgrid\b/, /\bblock\b/, 'grid and block'],
146
+ [/\bhidden\b/, /\bflex\b/, 'hidden and flex'],
147
+ [/\bhidden\b/, /\bgrid\b/, 'hidden and grid'],
148
+ [/\bhidden\b/, /\bblock\b/, 'hidden and block'],
149
+ [/\bstatic\b/, /\brelative\b/, 'static and relative'],
150
+ [/\bstatic\b/, /\babsolute\b/, 'static and absolute'],
151
+ [/\bstatic\b/, /\bfixed\b/, 'static and fixed'],
152
+ [/\bstatic\b/, /\bsticky\b/, 'static and sticky'],
153
+ [/\brelative\b/, /\babsolute\b/, 'relative and absolute'],
154
+ [/\brelative\b/, /\bfixed\b/, 'relative and fixed'],
155
+ [/\babsolute\b/, /\bfixed\b/, 'absolute and fixed'],
156
+ [/\bvisible\b/, /\binvisible\b/, 'visible and invisible'],
157
+ [/\bunderline\b/, /\bno-underline\b/, 'underline and no-underline'],
158
+ [/\buppercase\b/, /\blowercase\b/, 'uppercase and lowercase'],
159
+ [/\buppercase\b/, /\bcapitalize\b/, 'uppercase and capitalize'],
160
+ [/\blowercase\b/, /\bcapitalize\b/, 'lowercase and capitalize'],
161
+ ];
162
+ /**
163
+ * Recommended responsive breakpoint order
164
+ */
165
+ export const RESPONSIVE_BREAKPOINT_ORDER = ['sm', 'md', 'lg', 'xl', '2xl'];
166
+ /**
167
+ * Recommended class ordering categories
168
+ */
169
+ export const CLASS_ORDER_CATEGORIES = [
170
+ 'layout', // flex, grid, block, hidden
171
+ 'position', // relative, absolute, fixed, sticky
172
+ 'display', // overflow, z-index
173
+ 'sizing', // w-*, h-*, min-*, max-*
174
+ 'spacing', // p-*, m-*, gap-*
175
+ 'border', // border-*, rounded-*
176
+ 'background', // bg-*
177
+ 'typography', // text-*, font-*, leading-*
178
+ 'effects', // shadow-*, opacity-*
179
+ 'transitions', // transition-*, duration-*, ease-*
180
+ 'transforms', // scale-*, rotate-*, translate-*
181
+ ];
182
+ /**
183
+ * File patterns to exclude from Tailwind detection
184
+ */
185
+ export const EXCLUDED_FILE_PATTERNS = [
186
+ /\.test\.[jt]sx?$/,
187
+ /\.spec\.[jt]sx?$/,
188
+ /\.stories\.[jt]sx?$/,
189
+ /tailwind\.config\.[jt]s$/,
190
+ /postcss\.config\.[jt]s$/,
191
+ ];
192
+ // ============================================================================
193
+ // Helper Functions
194
+ // ============================================================================
195
+ /**
196
+ * Check if a file should be excluded from Tailwind detection
197
+ */
198
+ export function shouldExcludeFile(filePath) {
199
+ return EXCLUDED_FILE_PATTERNS.some(pattern => pattern.test(filePath));
200
+ }
201
+ /**
202
+ * Check if a position is inside a comment
203
+ */
204
+ function isInsideComment(content, index) {
205
+ const beforeIndex = content.slice(0, index);
206
+ // Check for single-line comment
207
+ const lastNewline = beforeIndex.lastIndexOf('\n');
208
+ const currentLine = beforeIndex.slice(lastNewline + 1);
209
+ if (currentLine.includes('//')) {
210
+ const commentStart = currentLine.indexOf('//');
211
+ const positionInLine = index - lastNewline - 1;
212
+ if (positionInLine > commentStart) {
213
+ return true;
214
+ }
215
+ }
216
+ // Check for multi-line comment
217
+ const lastBlockCommentStart = beforeIndex.lastIndexOf('/*');
218
+ const lastBlockCommentEnd = beforeIndex.lastIndexOf('*/');
219
+ if (lastBlockCommentStart > lastBlockCommentEnd) {
220
+ return true;
221
+ }
222
+ return false;
223
+ }
224
+ /**
225
+ * Check if a class is a standard Tailwind utility class
226
+ */
227
+ export function isTailwindUtilityClass(className) {
228
+ for (const pattern of TAILWIND_UTILITY_PATTERNS) {
229
+ const regex = new RegExp(pattern.source, pattern.flags);
230
+ if (regex.test(className)) {
231
+ return true;
232
+ }
233
+ }
234
+ return false;
235
+ }
236
+ /**
237
+ * Check if a class uses arbitrary values
238
+ */
239
+ export function isArbitraryValueClass(className) {
240
+ // Create new regex to avoid lastIndex issues with global flag
241
+ const regex = new RegExp(TAILWIND_ARBITRARY_VALUE_PATTERN.source, 'i');
242
+ return regex.test(className);
243
+ }
244
+ /**
245
+ * Check if a class has a responsive prefix
246
+ */
247
+ export function hasResponsivePrefix(className) {
248
+ // Create new regex to avoid lastIndex issues with global flag
249
+ const regex = new RegExp(TAILWIND_RESPONSIVE_PATTERN.source, 'i');
250
+ return regex.test(className);
251
+ }
252
+ /**
253
+ * Check if a class has a state variant
254
+ */
255
+ export function hasStateVariant(className) {
256
+ // Create new regex to avoid lastIndex issues with global flag
257
+ const regex = new RegExp(TAILWIND_STATE_VARIANT_PATTERN.source, 'i');
258
+ return regex.test(className);
259
+ }
260
+ /**
261
+ * Check if a class has dark mode prefix
262
+ */
263
+ export function hasDarkModePrefix(className) {
264
+ // Create new regex to avoid lastIndex issues with global flag
265
+ const regex = new RegExp(TAILWIND_DARK_MODE_PATTERN.source, 'i');
266
+ return regex.test(className);
267
+ }
268
+ /**
269
+ * Extract the base class from a prefixed class
270
+ * e.g., "sm:flex" -> "flex", "hover:bg-blue-500" -> "bg-blue-500"
271
+ */
272
+ export function extractBaseClass(className) {
273
+ // Remove responsive prefix
274
+ let base = className.replace(/^(sm|md|lg|xl|2xl):/, '');
275
+ // Remove state variant prefix
276
+ base = base.replace(new RegExp(`^(${TAILWIND_STATE_VARIANTS.join('|')}):`, 'i'), '');
277
+ // Remove dark mode prefix
278
+ base = base.replace(/^dark:/, '');
279
+ return base;
280
+ }
281
+ /**
282
+ * Get the responsive breakpoint from a class
283
+ */
284
+ export function getResponsiveBreakpoint(className) {
285
+ const match = className.match(/^(sm|md|lg|xl|2xl):/);
286
+ return match ? match[1] : null;
287
+ }
288
+ /**
289
+ * Get the breakpoint order index
290
+ */
291
+ export function getBreakpointOrderIndex(breakpoint) {
292
+ return RESPONSIVE_BREAKPOINT_ORDER.indexOf(breakpoint);
293
+ }
294
+ /**
295
+ * Detect standard Tailwind utility classes in content
296
+ */
297
+ export function detectUtilityClasses(content, file) {
298
+ const results = [];
299
+ const lines = content.split('\n');
300
+ const seenMatches = new Set();
301
+ for (const pattern of TAILWIND_UTILITY_PATTERNS) {
302
+ const regex = new RegExp(pattern.source, pattern.flags);
303
+ let match;
304
+ while ((match = regex.exec(content)) !== null) {
305
+ const key = `${match.index}-${match[0]}`;
306
+ if (seenMatches.has(key))
307
+ continue;
308
+ seenMatches.add(key);
309
+ // Skip if inside a comment
310
+ if (isInsideComment(content, match.index)) {
311
+ continue;
312
+ }
313
+ const beforeMatch = content.slice(0, match.index);
314
+ const lineNumber = beforeMatch.split('\n').length;
315
+ const lastNewline = beforeMatch.lastIndexOf('\n');
316
+ const column = match.index - lastNewline;
317
+ results.push({
318
+ type: 'utility-class',
319
+ file,
320
+ line: lineNumber,
321
+ column,
322
+ matchedText: match[0],
323
+ classNames: [match[0]],
324
+ context: lines[lineNumber - 1] || '',
325
+ });
326
+ }
327
+ }
328
+ return results;
329
+ }
330
+ /**
331
+ * Detect Tailwind arbitrary value classes
332
+ */
333
+ export function detectArbitraryValues(content, file) {
334
+ const results = [];
335
+ const lines = content.split('\n');
336
+ const regex = new RegExp(TAILWIND_ARBITRARY_VALUE_PATTERN.source, TAILWIND_ARBITRARY_VALUE_PATTERN.flags);
337
+ let match;
338
+ while ((match = regex.exec(content)) !== null) {
339
+ // Skip if inside a comment
340
+ if (isInsideComment(content, match.index)) {
341
+ continue;
342
+ }
343
+ const beforeMatch = content.slice(0, match.index);
344
+ const lineNumber = beforeMatch.split('\n').length;
345
+ const lastNewline = beforeMatch.lastIndexOf('\n');
346
+ const column = match.index - lastNewline;
347
+ results.push({
348
+ type: 'arbitrary-value',
349
+ file,
350
+ line: lineNumber,
351
+ column,
352
+ matchedText: match[0],
353
+ classNames: [match[0]],
354
+ context: lines[lineNumber - 1] || '',
355
+ });
356
+ }
357
+ return results;
358
+ }
359
+ /**
360
+ * Detect Tailwind responsive prefix usage
361
+ */
362
+ export function detectResponsivePrefixes(content, file) {
363
+ const results = [];
364
+ const lines = content.split('\n');
365
+ const regex = new RegExp(TAILWIND_RESPONSIVE_PATTERN.source, TAILWIND_RESPONSIVE_PATTERN.flags);
366
+ let match;
367
+ while ((match = regex.exec(content)) !== null) {
368
+ // Skip if inside a comment
369
+ if (isInsideComment(content, match.index)) {
370
+ continue;
371
+ }
372
+ const beforeMatch = content.slice(0, match.index);
373
+ const lineNumber = beforeMatch.split('\n').length;
374
+ const lastNewline = beforeMatch.lastIndexOf('\n');
375
+ const column = match.index - lastNewline;
376
+ results.push({
377
+ type: 'responsive-prefix',
378
+ file,
379
+ line: lineNumber,
380
+ column,
381
+ matchedText: match[0],
382
+ classNames: [match[0]],
383
+ context: lines[lineNumber - 1] || '',
384
+ });
385
+ }
386
+ return results;
387
+ }
388
+ /**
389
+ * Detect Tailwind state variant usage
390
+ */
391
+ export function detectStateVariants(content, file) {
392
+ const results = [];
393
+ const lines = content.split('\n');
394
+ const regex = new RegExp(TAILWIND_STATE_VARIANT_PATTERN.source, TAILWIND_STATE_VARIANT_PATTERN.flags);
395
+ let match;
396
+ while ((match = regex.exec(content)) !== null) {
397
+ // Skip if inside a comment
398
+ if (isInsideComment(content, match.index)) {
399
+ continue;
400
+ }
401
+ const beforeMatch = content.slice(0, match.index);
402
+ const lineNumber = beforeMatch.split('\n').length;
403
+ const lastNewline = beforeMatch.lastIndexOf('\n');
404
+ const column = match.index - lastNewline;
405
+ results.push({
406
+ type: 'state-variant',
407
+ file,
408
+ line: lineNumber,
409
+ column,
410
+ matchedText: match[0],
411
+ classNames: [match[0]],
412
+ context: lines[lineNumber - 1] || '',
413
+ });
414
+ }
415
+ return results;
416
+ }
417
+ /**
418
+ * Detect Tailwind dark mode usage
419
+ */
420
+ export function detectDarkMode(content, file) {
421
+ const results = [];
422
+ const lines = content.split('\n');
423
+ const regex = new RegExp(TAILWIND_DARK_MODE_PATTERN.source, TAILWIND_DARK_MODE_PATTERN.flags);
424
+ let match;
425
+ while ((match = regex.exec(content)) !== null) {
426
+ // Skip if inside a comment
427
+ if (isInsideComment(content, match.index)) {
428
+ continue;
429
+ }
430
+ const beforeMatch = content.slice(0, match.index);
431
+ const lineNumber = beforeMatch.split('\n').length;
432
+ const lastNewline = beforeMatch.lastIndexOf('\n');
433
+ const column = match.index - lastNewline;
434
+ results.push({
435
+ type: 'dark-mode',
436
+ file,
437
+ line: lineNumber,
438
+ column,
439
+ matchedText: match[0],
440
+ classNames: [match[0]],
441
+ context: lines[lineNumber - 1] || '',
442
+ });
443
+ }
444
+ return results;
445
+ }
446
+ /**
447
+ * Detect @apply directive usage
448
+ */
449
+ export function detectApplyDirective(content, file) {
450
+ const results = [];
451
+ const lines = content.split('\n');
452
+ const regex = new RegExp(TAILWIND_APPLY_PATTERN.source, TAILWIND_APPLY_PATTERN.flags);
453
+ let match;
454
+ while ((match = regex.exec(content)) !== null) {
455
+ // Skip if inside a comment
456
+ if (isInsideComment(content, match.index)) {
457
+ continue;
458
+ }
459
+ const beforeMatch = content.slice(0, match.index);
460
+ const lineNumber = beforeMatch.split('\n').length;
461
+ const lastNewline = beforeMatch.lastIndexOf('\n');
462
+ const column = match.index - lastNewline;
463
+ const classes = match[1]?.trim().split(/\s+/) || [];
464
+ results.push({
465
+ type: 'apply-directive',
466
+ file,
467
+ line: lineNumber,
468
+ column,
469
+ matchedText: match[0],
470
+ classNames: classes,
471
+ context: lines[lineNumber - 1] || '',
472
+ });
473
+ }
474
+ return results;
475
+ }
476
+ /**
477
+ * Detect Tailwind config customizations
478
+ */
479
+ export function detectConfigCustomizations(content, file) {
480
+ const results = [];
481
+ const lines = content.split('\n');
482
+ // Only check config files
483
+ if (!file.includes('tailwind.config')) {
484
+ return results;
485
+ }
486
+ for (const pattern of TAILWIND_CONFIG_PATTERNS) {
487
+ const regex = new RegExp(pattern.source, pattern.flags);
488
+ let match;
489
+ while ((match = regex.exec(content)) !== null) {
490
+ const beforeMatch = content.slice(0, match.index);
491
+ const lineNumber = beforeMatch.split('\n').length;
492
+ const lastNewline = beforeMatch.lastIndexOf('\n');
493
+ const column = match.index - lastNewline;
494
+ results.push({
495
+ type: 'config-customization',
496
+ file,
497
+ line: lineNumber,
498
+ column,
499
+ matchedText: match[0],
500
+ classNames: [],
501
+ context: lines[lineNumber - 1] || '',
502
+ });
503
+ }
504
+ }
505
+ return results;
506
+ }
507
+ /**
508
+ * Extract all class names from a className attribute
509
+ */
510
+ function extractClassNamesFromLine(line) {
511
+ const classNames = [];
512
+ // Match className="..." or class="..."
513
+ const classAttrMatch = line.match(/(?:className|class)=["']([^"']+)["']/);
514
+ if (classAttrMatch && classAttrMatch[1]) {
515
+ classNames.push(...classAttrMatch[1].split(/\s+/).filter(Boolean));
516
+ }
517
+ // Match className={`...`} template literals
518
+ const templateMatch = line.match(/(?:className|class)=\{`([^`]+)`\}/);
519
+ if (templateMatch && templateMatch[1]) {
520
+ // Extract static parts (ignore ${...} expressions)
521
+ const staticParts = templateMatch[1].replace(/\$\{[^}]+\}/g, ' ');
522
+ classNames.push(...staticParts.split(/\s+/).filter(Boolean));
523
+ }
524
+ // Match @apply directive
525
+ const applyMatch = line.match(/@apply\s+([^;]+)/);
526
+ if (applyMatch && applyMatch[1]) {
527
+ classNames.push(...applyMatch[1].split(/\s+/).filter(Boolean));
528
+ }
529
+ return classNames;
530
+ }
531
+ /**
532
+ * Detect arbitrary value usage violations
533
+ */
534
+ export function detectArbitraryValueViolations(arbitraryPatterns, utilityPatterns, file) {
535
+ const results = [];
536
+ for (const arbitrary of arbitraryPatterns) {
537
+ const baseProperty = arbitrary.matchedText.split('-[')[0];
538
+ // Check if there's a standard utility for this property
539
+ const hasStandardAlternative = utilityPatterns.some(utility => {
540
+ const utilityBase = utility.matchedText.split('-')[0];
541
+ return utilityBase === baseProperty;
542
+ });
543
+ // Suggest using standard classes when possible
544
+ const suggestedFix = suggestStandardClass(arbitrary.matchedText);
545
+ const finalSuggestedFix = suggestedFix || (hasStandardAlternative
546
+ ? `Consider using a standard Tailwind class for '${baseProperty}'`
547
+ : undefined);
548
+ const violation = {
549
+ type: 'arbitrary-value-usage',
550
+ file,
551
+ line: arbitrary.line,
552
+ column: arbitrary.column,
553
+ endLine: arbitrary.line,
554
+ endColumn: arbitrary.column + arbitrary.matchedText.length,
555
+ classNames: [arbitrary.matchedText],
556
+ issue: `Arbitrary value '${arbitrary.matchedText}' used instead of standard Tailwind class`,
557
+ lineContent: arbitrary.context || '',
558
+ };
559
+ if (finalSuggestedFix !== undefined) {
560
+ violation.suggestedFix = finalSuggestedFix;
561
+ }
562
+ results.push(violation);
563
+ }
564
+ return results;
565
+ }
566
+ /**
567
+ * Suggest a standard Tailwind class for an arbitrary value
568
+ */
569
+ export function suggestStandardClass(arbitraryClass) {
570
+ const match = arbitraryClass.match(/^([a-z][a-z0-9-]*)-\[([^\]]+)\]$/i);
571
+ if (!match)
572
+ return undefined;
573
+ const [, property, value] = match;
574
+ // Handle pixel values
575
+ const pxMatch = value?.match(/^(\d+)px$/);
576
+ if (pxMatch && property) {
577
+ const px = parseInt(pxMatch[1], 10);
578
+ // Tailwind uses 4px = 1 unit
579
+ const tailwindUnit = px / 4;
580
+ if (Number.isInteger(tailwindUnit) && tailwindUnit >= 0 && tailwindUnit <= 96) {
581
+ return `${property}-${tailwindUnit}`;
582
+ }
583
+ }
584
+ // Handle rem values
585
+ const remMatch = value?.match(/^(\d+(?:\.\d+)?)rem$/);
586
+ if (remMatch && property) {
587
+ const rem = parseFloat(remMatch[1]);
588
+ // Tailwind uses 0.25rem = 1 unit
589
+ const tailwindUnit = rem / 0.25;
590
+ if (Number.isInteger(tailwindUnit) && tailwindUnit >= 0 && tailwindUnit <= 96) {
591
+ return `${property}-${tailwindUnit}`;
592
+ }
593
+ }
594
+ // Handle percentage values for width/height
595
+ if ((property === 'w' || property === 'h') && value) {
596
+ if (value === '100%')
597
+ return `${property}-full`;
598
+ if (value === '50%')
599
+ return `${property}-1/2`;
600
+ if (value === '33.333333%' || value === '33.33%')
601
+ return `${property}-1/3`;
602
+ if (value === '66.666667%' || value === '66.67%')
603
+ return `${property}-2/3`;
604
+ if (value === '25%')
605
+ return `${property}-1/4`;
606
+ if (value === '75%')
607
+ return `${property}-3/4`;
608
+ }
609
+ return undefined;
610
+ }
611
+ /**
612
+ * Detect conflicting class violations
613
+ */
614
+ export function detectConflictingClasses(content, file) {
615
+ const results = [];
616
+ const lines = content.split('\n');
617
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
618
+ const line = lines[lineIndex];
619
+ const classNames = extractClassNamesFromLine(line);
620
+ if (classNames.length === 0)
621
+ continue;
622
+ // Check for conflicting pairs
623
+ for (const [pattern1, pattern2, description] of CONFLICTING_CLASS_PAIRS) {
624
+ const hasFirst = classNames.some(cn => pattern1.test(cn));
625
+ const hasSecond = classNames.some(cn => pattern2.test(cn));
626
+ if (hasFirst && hasSecond) {
627
+ const conflictingClasses = classNames.filter(cn => pattern1.test(cn) || pattern2.test(cn));
628
+ // Find the position of the first conflicting class
629
+ const firstClass = conflictingClasses[0];
630
+ const classIndex = line.indexOf(firstClass);
631
+ const column = classIndex >= 0 ? classIndex + 1 : 1;
632
+ results.push({
633
+ type: 'conflicting-classes',
634
+ file,
635
+ line: lineIndex + 1,
636
+ column,
637
+ endLine: lineIndex + 1,
638
+ endColumn: column + conflictingClasses.join(' ').length,
639
+ classNames: conflictingClasses,
640
+ issue: `Conflicting classes: ${description}`,
641
+ suggestedFix: `Remove one of the conflicting classes: ${conflictingClasses.join(', ')}`,
642
+ lineContent: line,
643
+ });
644
+ }
645
+ }
646
+ }
647
+ return results;
648
+ }
649
+ /**
650
+ * Detect inconsistent responsive breakpoint ordering
651
+ */
652
+ export function detectInconsistentBreakpointOrder(content, file) {
653
+ const results = [];
654
+ const lines = content.split('\n');
655
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
656
+ const line = lines[lineIndex];
657
+ const classNames = extractClassNamesFromLine(line);
658
+ if (classNames.length === 0)
659
+ continue;
660
+ // Group classes by their property prefix (e.g., 'p' for p-4, 'flex' for flex)
661
+ const classGroups = new Map();
662
+ classNames.forEach((className, index) => {
663
+ const breakpoint = getResponsiveBreakpoint(className);
664
+ const baseClass = extractBaseClass(className);
665
+ // Extract property prefix (e.g., 'p' from 'p-4', 'flex' from 'flex')
666
+ const propertyMatch = baseClass.match(/^([a-z]+(?:-[a-z]+)*?)(?:-\d|$)/i);
667
+ const propertyPrefix = propertyMatch ? propertyMatch[1] : baseClass;
668
+ if (!classGroups.has(propertyPrefix)) {
669
+ classGroups.set(propertyPrefix, []);
670
+ }
671
+ classGroups.get(propertyPrefix).push({ className, breakpoint, index });
672
+ });
673
+ // Check each group for proper ordering
674
+ for (const [propertyPrefix, group] of classGroups) {
675
+ const responsiveClasses = group.filter(g => g.breakpoint !== null);
676
+ if (responsiveClasses.length < 2)
677
+ continue;
678
+ // Check if breakpoints are in order (by their position in the class list)
679
+ // Sort by their original index to check order
680
+ const sortedByIndex = [...responsiveClasses].sort((a, b) => a.index - b.index);
681
+ let lastBreakpointIndex = -1;
682
+ let isOutOfOrder = false;
683
+ let outOfOrderClasses = [];
684
+ for (const { breakpoint } of sortedByIndex) {
685
+ const breakpointIndex = getBreakpointOrderIndex(breakpoint);
686
+ if (breakpointIndex < lastBreakpointIndex) {
687
+ isOutOfOrder = true;
688
+ outOfOrderClasses = sortedByIndex.map(r => r.className);
689
+ break;
690
+ }
691
+ lastBreakpointIndex = breakpointIndex;
692
+ }
693
+ if (isOutOfOrder) {
694
+ const firstClass = outOfOrderClasses[0];
695
+ const classIndex = line.indexOf(firstClass);
696
+ const column = classIndex >= 0 ? classIndex + 1 : 1;
697
+ // Sort classes by breakpoint order
698
+ const sortedClasses = [...responsiveClasses]
699
+ .sort((a, b) => getBreakpointOrderIndex(a.breakpoint) - getBreakpointOrderIndex(b.breakpoint))
700
+ .map(r => r.className);
701
+ results.push({
702
+ type: 'inconsistent-breakpoint-order',
703
+ file,
704
+ line: lineIndex + 1,
705
+ column,
706
+ endLine: lineIndex + 1,
707
+ endColumn: column + outOfOrderClasses.join(' ').length,
708
+ classNames: outOfOrderClasses,
709
+ issue: `Responsive breakpoints for '${propertyPrefix}' are not in mobile-first order (sm → md → lg → xl → 2xl)`,
710
+ suggestedFix: `Reorder to: ${sortedClasses.join(' ')}`,
711
+ lineContent: line,
712
+ });
713
+ }
714
+ }
715
+ }
716
+ return results;
717
+ }
718
+ /**
719
+ * Detect mixed arbitrary and standard values for the same property
720
+ */
721
+ export function detectMixedArbitraryStandard(arbitraryPatterns, utilityPatterns, file) {
722
+ const results = [];
723
+ // Group patterns by property prefix
724
+ const arbitraryByProperty = new Map();
725
+ const standardByProperty = new Map();
726
+ for (const pattern of arbitraryPatterns) {
727
+ const property = pattern.matchedText.split('-[')[0] || '';
728
+ if (!arbitraryByProperty.has(property)) {
729
+ arbitraryByProperty.set(property, []);
730
+ }
731
+ arbitraryByProperty.get(property).push(pattern);
732
+ }
733
+ for (const pattern of utilityPatterns) {
734
+ const property = pattern.matchedText.split('-')[0] || '';
735
+ if (!standardByProperty.has(property)) {
736
+ standardByProperty.set(property, []);
737
+ }
738
+ standardByProperty.get(property).push(pattern);
739
+ }
740
+ // Find properties that have both arbitrary and standard values
741
+ for (const [property, arbitraryList] of arbitraryByProperty) {
742
+ if (standardByProperty.has(property)) {
743
+ const standardList = standardByProperty.get(property);
744
+ // Only flag if there are multiple instances suggesting inconsistency
745
+ if (arbitraryList.length > 0 && standardList.length > 0) {
746
+ const firstArbitrary = arbitraryList[0];
747
+ results.push({
748
+ type: 'mixed-arbitrary-standard',
749
+ file,
750
+ line: firstArbitrary.line,
751
+ column: firstArbitrary.column,
752
+ endLine: firstArbitrary.line,
753
+ endColumn: firstArbitrary.column + firstArbitrary.matchedText.length,
754
+ classNames: [
755
+ ...arbitraryList.map(p => p.matchedText),
756
+ ...standardList.map(p => p.matchedText),
757
+ ],
758
+ issue: `Mixed arbitrary and standard values for '${property}' property`,
759
+ suggestedFix: `Consider using consistent values - either all standard Tailwind classes or all arbitrary values for '${property}'`,
760
+ lineContent: firstArbitrary.context || '',
761
+ });
762
+ }
763
+ }
764
+ }
765
+ return results;
766
+ }
767
+ /**
768
+ * Analyze Tailwind patterns in a file
769
+ */
770
+ export function analyzeTailwindPatterns(content, file) {
771
+ // Skip excluded files
772
+ if (shouldExcludeFile(file)) {
773
+ return {
774
+ patterns: [],
775
+ violations: [],
776
+ usesUtilityClasses: false,
777
+ usesArbitraryValues: false,
778
+ usesResponsivePrefixes: false,
779
+ usesStateVariants: false,
780
+ usesDarkMode: false,
781
+ usesApplyDirective: false,
782
+ tailwindConsistencyConfidence: 1.0,
783
+ };
784
+ }
785
+ // Detect all patterns
786
+ const utilityClasses = detectUtilityClasses(content, file);
787
+ const arbitraryValues = detectArbitraryValues(content, file);
788
+ const responsivePrefixes = detectResponsivePrefixes(content, file);
789
+ const stateVariants = detectStateVariants(content, file);
790
+ const darkMode = detectDarkMode(content, file);
791
+ const applyDirective = detectApplyDirective(content, file);
792
+ const configCustomizations = detectConfigCustomizations(content, file);
793
+ const allPatterns = [
794
+ ...utilityClasses,
795
+ ...arbitraryValues,
796
+ ...responsivePrefixes,
797
+ ...stateVariants,
798
+ ...darkMode,
799
+ ...applyDirective,
800
+ ...configCustomizations,
801
+ ];
802
+ // Detect violations
803
+ const arbitraryViolations = detectArbitraryValueViolations(arbitraryValues, utilityClasses, file);
804
+ const conflictingViolations = detectConflictingClasses(content, file);
805
+ const breakpointViolations = detectInconsistentBreakpointOrder(content, file);
806
+ const mixedViolations = detectMixedArbitraryStandard(arbitraryValues, utilityClasses, file);
807
+ const allViolations = [
808
+ ...arbitraryViolations,
809
+ ...conflictingViolations,
810
+ ...breakpointViolations,
811
+ ...mixedViolations,
812
+ ];
813
+ // Calculate confidence
814
+ const hasPatterns = allPatterns.length > 0;
815
+ const hasViolations = allViolations.length > 0;
816
+ let tailwindConsistencyConfidence = 0;
817
+ if (hasPatterns && !hasViolations) {
818
+ tailwindConsistencyConfidence = 1.0;
819
+ }
820
+ else if (hasPatterns && hasViolations) {
821
+ const ratio = allPatterns.length / (allPatterns.length + allViolations.length);
822
+ tailwindConsistencyConfidence = ratio;
823
+ }
824
+ else if (!hasPatterns && hasViolations) {
825
+ tailwindConsistencyConfidence = 0;
826
+ }
827
+ else {
828
+ tailwindConsistencyConfidence = 0.5; // No Tailwind detected
829
+ }
830
+ return {
831
+ patterns: allPatterns,
832
+ violations: allViolations,
833
+ usesUtilityClasses: utilityClasses.length > 0,
834
+ usesArbitraryValues: arbitraryValues.length > 0,
835
+ usesResponsivePrefixes: responsivePrefixes.length > 0,
836
+ usesStateVariants: stateVariants.length > 0,
837
+ usesDarkMode: darkMode.length > 0,
838
+ usesApplyDirective: applyDirective.length > 0,
839
+ tailwindConsistencyConfidence,
840
+ };
841
+ }
842
+ // ============================================================================
843
+ // Tailwind Patterns Detector Class
844
+ // ============================================================================
845
+ /**
846
+ * Detector for Tailwind CSS pattern consistency
847
+ *
848
+ * Identifies Tailwind patterns and flags inconsistent usage.
849
+ *
850
+ * @requirements 9.6 - THE Styling_Detector SHALL detect Tailwind pattern consistency
851
+ */
852
+ export class TailwindPatternsDetector extends RegexDetector {
853
+ id = 'styling/tailwind-patterns';
854
+ category = 'styling';
855
+ subcategory = 'tailwind-patterns';
856
+ name = 'Tailwind Patterns Detector';
857
+ description = 'Detects Tailwind CSS patterns and flags inconsistent usage';
858
+ supportedLanguages = ['typescript', 'javascript', 'css'];
859
+ /**
860
+ * Detect Tailwind patterns and violations
861
+ */
862
+ async detect(context) {
863
+ const patterns = [];
864
+ const violations = [];
865
+ // Analyze the file
866
+ const analysis = analyzeTailwindPatterns(context.content, context.file);
867
+ // Create pattern matches for Tailwind patterns
868
+ if (analysis.usesUtilityClasses) {
869
+ patterns.push(this.createUtilityClassPattern(context.file, analysis));
870
+ }
871
+ if (analysis.usesArbitraryValues) {
872
+ patterns.push(this.createArbitraryValuePattern(context.file, analysis));
873
+ }
874
+ if (analysis.usesResponsivePrefixes) {
875
+ patterns.push(this.createResponsivePrefixPattern(context.file, analysis));
876
+ }
877
+ if (analysis.usesStateVariants) {
878
+ patterns.push(this.createStateVariantPattern(context.file, analysis));
879
+ }
880
+ if (analysis.usesDarkMode) {
881
+ patterns.push(this.createDarkModePattern(context.file, analysis));
882
+ }
883
+ if (analysis.usesApplyDirective) {
884
+ patterns.push(this.createApplyDirectivePattern(context.file, analysis));
885
+ }
886
+ // Create violations
887
+ for (const violation of analysis.violations) {
888
+ violations.push(this.createTailwindViolation(violation));
889
+ }
890
+ return this.createResult(patterns, violations, analysis.tailwindConsistencyConfidence);
891
+ }
892
+ /**
893
+ * Create a pattern match for utility class usage
894
+ */
895
+ createUtilityClassPattern(file, analysis) {
896
+ const utilityPatterns = analysis.patterns.filter(p => p.type === 'utility-class');
897
+ const firstPattern = utilityPatterns[0];
898
+ return {
899
+ patternId: `${this.id}/utility-class`,
900
+ location: {
901
+ file,
902
+ line: firstPattern?.line || 1,
903
+ column: firstPattern?.column || 1,
904
+ },
905
+ confidence: 1.0,
906
+ isOutlier: false,
907
+ };
908
+ }
909
+ /**
910
+ * Create a pattern match for arbitrary value usage
911
+ */
912
+ createArbitraryValuePattern(file, analysis) {
913
+ const arbitraryPatterns = analysis.patterns.filter(p => p.type === 'arbitrary-value');
914
+ const firstPattern = arbitraryPatterns[0];
915
+ return {
916
+ patternId: `${this.id}/arbitrary-value`,
917
+ location: {
918
+ file,
919
+ line: firstPattern?.line || 1,
920
+ column: firstPattern?.column || 1,
921
+ },
922
+ confidence: 0.7, // Lower confidence for arbitrary values
923
+ isOutlier: true, // Arbitrary values are considered outliers
924
+ };
925
+ }
926
+ /**
927
+ * Create a pattern match for responsive prefix usage
928
+ */
929
+ createResponsivePrefixPattern(file, analysis) {
930
+ const responsivePatterns = analysis.patterns.filter(p => p.type === 'responsive-prefix');
931
+ const firstPattern = responsivePatterns[0];
932
+ return {
933
+ patternId: `${this.id}/responsive-prefix`,
934
+ location: {
935
+ file,
936
+ line: firstPattern?.line || 1,
937
+ column: firstPattern?.column || 1,
938
+ },
939
+ confidence: 1.0,
940
+ isOutlier: false,
941
+ };
942
+ }
943
+ /**
944
+ * Create a pattern match for state variant usage
945
+ */
946
+ createStateVariantPattern(file, analysis) {
947
+ const statePatterns = analysis.patterns.filter(p => p.type === 'state-variant');
948
+ const firstPattern = statePatterns[0];
949
+ return {
950
+ patternId: `${this.id}/state-variant`,
951
+ location: {
952
+ file,
953
+ line: firstPattern?.line || 1,
954
+ column: firstPattern?.column || 1,
955
+ },
956
+ confidence: 1.0,
957
+ isOutlier: false,
958
+ };
959
+ }
960
+ /**
961
+ * Create a pattern match for dark mode usage
962
+ */
963
+ createDarkModePattern(file, analysis) {
964
+ const darkModePatterns = analysis.patterns.filter(p => p.type === 'dark-mode');
965
+ const firstPattern = darkModePatterns[0];
966
+ return {
967
+ patternId: `${this.id}/dark-mode`,
968
+ location: {
969
+ file,
970
+ line: firstPattern?.line || 1,
971
+ column: firstPattern?.column || 1,
972
+ },
973
+ confidence: 1.0,
974
+ isOutlier: false,
975
+ };
976
+ }
977
+ /**
978
+ * Create a pattern match for @apply directive usage
979
+ */
980
+ createApplyDirectivePattern(file, analysis) {
981
+ const applyPatterns = analysis.patterns.filter(p => p.type === 'apply-directive');
982
+ const firstPattern = applyPatterns[0];
983
+ return {
984
+ patternId: `${this.id}/apply-directive`,
985
+ location: {
986
+ file,
987
+ line: firstPattern?.line || 1,
988
+ column: firstPattern?.column || 1,
989
+ },
990
+ confidence: 1.0,
991
+ isOutlier: false,
992
+ };
993
+ }
994
+ /**
995
+ * Create a violation for a Tailwind issue
996
+ */
997
+ createTailwindViolation(info) {
998
+ const typeDescriptions = {
999
+ 'arbitrary-value-usage': 'Arbitrary value usage',
1000
+ 'inconsistent-breakpoint-order': 'Inconsistent breakpoint order',
1001
+ 'conflicting-classes': 'Conflicting classes',
1002
+ 'non-standard-ordering': 'Non-standard class ordering',
1003
+ 'mixed-arbitrary-standard': 'Mixed arbitrary and standard values',
1004
+ };
1005
+ const typeDescription = typeDescriptions[info.type] || 'Tailwind issue';
1006
+ const severity = this.getSeverityForViolationType(info.type);
1007
+ const violation = {
1008
+ id: `${this.id}-${info.file}-${info.line}-${info.column}`,
1009
+ patternId: this.id,
1010
+ severity,
1011
+ file: info.file,
1012
+ range: {
1013
+ start: { line: info.line - 1, character: info.column - 1 },
1014
+ end: { line: info.endLine - 1, character: info.endColumn - 1 },
1015
+ },
1016
+ message: `${typeDescription}: ${info.issue}`,
1017
+ explanation: this.getExplanationForViolationType(info.type),
1018
+ expected: info.suggestedFix || 'Consistent Tailwind usage',
1019
+ actual: info.classNames.join(', '),
1020
+ aiExplainAvailable: true,
1021
+ aiFixAvailable: info.type === 'conflicting-classes' || info.type === 'inconsistent-breakpoint-order',
1022
+ firstSeen: new Date(),
1023
+ occurrences: 1,
1024
+ };
1025
+ const quickFix = this.createQuickFixForViolation(info);
1026
+ if (quickFix !== undefined) {
1027
+ violation.quickFix = quickFix;
1028
+ }
1029
+ return violation;
1030
+ }
1031
+ /**
1032
+ * Get severity for a violation type
1033
+ */
1034
+ getSeverityForViolationType(type) {
1035
+ switch (type) {
1036
+ case 'conflicting-classes':
1037
+ return 'warning';
1038
+ case 'inconsistent-breakpoint-order':
1039
+ return 'info';
1040
+ case 'arbitrary-value-usage':
1041
+ return 'info';
1042
+ case 'mixed-arbitrary-standard':
1043
+ return 'info';
1044
+ case 'non-standard-ordering':
1045
+ return 'hint';
1046
+ default:
1047
+ return 'info';
1048
+ }
1049
+ }
1050
+ /**
1051
+ * Get explanation for a violation type
1052
+ */
1053
+ getExplanationForViolationType(type) {
1054
+ switch (type) {
1055
+ case 'arbitrary-value-usage':
1056
+ return 'Using arbitrary values (e.g., p-[13px]) instead of standard Tailwind classes can lead to inconsistent spacing and make the design system harder to maintain. Consider using standard Tailwind spacing values.';
1057
+ case 'inconsistent-breakpoint-order':
1058
+ return 'Tailwind uses a mobile-first approach. Responsive classes should be ordered from smallest to largest breakpoint (sm → md → lg → xl → 2xl) for better readability and maintainability.';
1059
+ case 'conflicting-classes':
1060
+ return 'These classes conflict with each other and may cause unexpected behavior. Only one of these classes should be applied at a time.';
1061
+ case 'non-standard-ordering':
1062
+ return 'Following a consistent class ordering convention improves readability. Consider ordering classes by: layout → position → sizing → spacing → border → background → typography → effects.';
1063
+ case 'mixed-arbitrary-standard':
1064
+ return 'Mixing arbitrary values with standard Tailwind classes for the same property can lead to inconsistent styling. Consider using either all standard classes or all arbitrary values consistently.';
1065
+ default:
1066
+ return 'Consistent Tailwind usage improves code maintainability and design system adherence.';
1067
+ }
1068
+ }
1069
+ /**
1070
+ * Create a quick fix for a Tailwind violation
1071
+ */
1072
+ createQuickFixForViolation(info) {
1073
+ if (info.type === 'inconsistent-breakpoint-order' && info.suggestedFix) {
1074
+ return {
1075
+ title: `Reorder breakpoints: ${info.suggestedFix}`,
1076
+ kind: 'quickfix',
1077
+ edit: {
1078
+ changes: {
1079
+ [info.file]: [
1080
+ {
1081
+ range: {
1082
+ start: { line: info.line - 1, character: info.column - 1 },
1083
+ end: { line: info.endLine - 1, character: info.endColumn - 1 },
1084
+ },
1085
+ newText: info.suggestedFix,
1086
+ },
1087
+ ],
1088
+ },
1089
+ },
1090
+ isPreferred: true,
1091
+ confidence: 0.9,
1092
+ preview: `Reorder to: ${info.suggestedFix}`,
1093
+ };
1094
+ }
1095
+ if (info.type === 'arbitrary-value-usage' && info.suggestedFix) {
1096
+ const standardClass = suggestStandardClass(info.classNames[0] || '');
1097
+ if (standardClass) {
1098
+ return {
1099
+ title: `Use standard class: ${standardClass}`,
1100
+ kind: 'quickfix',
1101
+ edit: {
1102
+ changes: {
1103
+ [info.file]: [
1104
+ {
1105
+ range: {
1106
+ start: { line: info.line - 1, character: info.column - 1 },
1107
+ end: { line: info.endLine - 1, character: info.endColumn - 1 },
1108
+ },
1109
+ newText: standardClass,
1110
+ },
1111
+ ],
1112
+ },
1113
+ },
1114
+ isPreferred: true,
1115
+ confidence: 0.8,
1116
+ preview: `Replace '${info.classNames[0]}' with '${standardClass}'`,
1117
+ };
1118
+ }
1119
+ }
1120
+ return undefined;
1121
+ }
1122
+ /**
1123
+ * Generate a quick fix for a violation
1124
+ */
1125
+ generateQuickFix(violation) {
1126
+ // Check if this is a breakpoint order violation
1127
+ if (violation.message.includes('breakpoint order') && violation.expected !== 'Consistent Tailwind usage') {
1128
+ return {
1129
+ title: `Reorder breakpoints`,
1130
+ kind: 'quickfix',
1131
+ edit: {
1132
+ changes: {
1133
+ [violation.file]: [
1134
+ {
1135
+ range: violation.range,
1136
+ newText: violation.expected,
1137
+ },
1138
+ ],
1139
+ },
1140
+ },
1141
+ isPreferred: true,
1142
+ confidence: 0.9,
1143
+ preview: `Reorder to: ${violation.expected}`,
1144
+ };
1145
+ }
1146
+ // Check if this is an arbitrary value violation with a suggestion
1147
+ if (violation.message.includes('Arbitrary value') && violation.actual) {
1148
+ const standardClass = suggestStandardClass(violation.actual);
1149
+ if (standardClass) {
1150
+ return {
1151
+ title: `Use standard class: ${standardClass}`,
1152
+ kind: 'quickfix',
1153
+ edit: {
1154
+ changes: {
1155
+ [violation.file]: [
1156
+ {
1157
+ range: violation.range,
1158
+ newText: standardClass,
1159
+ },
1160
+ ],
1161
+ },
1162
+ },
1163
+ isPreferred: true,
1164
+ confidence: 0.8,
1165
+ preview: `Replace '${violation.actual}' with '${standardClass}'`,
1166
+ };
1167
+ }
1168
+ }
1169
+ return null;
1170
+ }
1171
+ }
1172
+ // ============================================================================
1173
+ // Factory Function
1174
+ // ============================================================================
1175
+ /**
1176
+ * Create a new TailwindPatternsDetector instance
1177
+ */
1178
+ export function createTailwindPatternsDetector() {
1179
+ return new TailwindPatternsDetector();
1180
+ }
1181
+ //# sourceMappingURL=tailwind-patterns.js.map