@empline/preflight 1.1.11 → 1.1.13

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 (709) hide show
  1. package/dist/checks/consolidated/auth-storage-state.d.ts +3 -0
  2. package/dist/checks/consolidated/auth-storage-state.d.ts.map +1 -0
  3. package/dist/checks/consolidated/auth-storage-state.js +146 -0
  4. package/dist/checks/consolidated/auth-storage-state.js.map +1 -0
  5. package/dist/checks/consolidated/business.d.ts +50 -0
  6. package/dist/checks/consolidated/business.d.ts.map +1 -0
  7. package/dist/checks/consolidated/business.js +252 -0
  8. package/dist/checks/consolidated/business.js.map +1 -0
  9. package/dist/checks/consolidated/caching-strategy.d.ts +104 -0
  10. package/dist/checks/consolidated/caching-strategy.d.ts.map +1 -0
  11. package/dist/checks/consolidated/caching-strategy.js +725 -0
  12. package/dist/checks/consolidated/caching-strategy.js.map +1 -0
  13. package/dist/checks/consolidated/code-quality.d.ts +83 -0
  14. package/dist/checks/consolidated/code-quality.d.ts.map +1 -0
  15. package/dist/checks/consolidated/code-quality.js +445 -0
  16. package/dist/checks/consolidated/code-quality.js.map +1 -0
  17. package/dist/checks/consolidated/console-statements.d.ts +32 -0
  18. package/dist/checks/consolidated/console-statements.d.ts.map +1 -0
  19. package/dist/checks/consolidated/console-statements.js +304 -0
  20. package/dist/checks/consolidated/console-statements.js.map +1 -0
  21. package/dist/checks/consolidated/css-advanced-validation.d.ts +24 -0
  22. package/dist/checks/consolidated/css-advanced-validation.d.ts.map +1 -0
  23. package/dist/checks/consolidated/css-advanced-validation.js +415 -0
  24. package/dist/checks/consolidated/css-advanced-validation.js.map +1 -0
  25. package/dist/checks/consolidated/css-organization.d.ts +14 -0
  26. package/dist/checks/consolidated/css-organization.d.ts.map +1 -0
  27. package/dist/checks/consolidated/css-organization.js +432 -0
  28. package/dist/checks/consolidated/css-organization.js.map +1 -0
  29. package/dist/checks/consolidated/css-runtime-validation.d.ts +22 -0
  30. package/dist/checks/consolidated/css-runtime-validation.d.ts.map +1 -0
  31. package/dist/checks/consolidated/css-runtime-validation.js +330 -0
  32. package/dist/checks/consolidated/css-runtime-validation.js.map +1 -0
  33. package/dist/checks/consolidated/css-variable-validation.d.ts +17 -0
  34. package/dist/checks/consolidated/css-variable-validation.d.ts.map +1 -0
  35. package/dist/checks/consolidated/css-variable-validation.js +412 -0
  36. package/dist/checks/consolidated/css-variable-validation.js.map +1 -0
  37. package/dist/checks/consolidated/dark-mode-consistency.d.ts +23 -0
  38. package/dist/checks/consolidated/dark-mode-consistency.d.ts.map +1 -0
  39. package/dist/checks/consolidated/dark-mode-consistency.js +291 -0
  40. package/dist/checks/consolidated/dark-mode-consistency.js.map +1 -0
  41. package/dist/checks/consolidated/database.d.ts +95 -0
  42. package/dist/checks/consolidated/database.d.ts.map +1 -0
  43. package/dist/checks/consolidated/database.js +427 -0
  44. package/dist/checks/consolidated/database.js.map +1 -0
  45. package/dist/checks/consolidated/e2e-checks.d.ts +52 -0
  46. package/dist/checks/consolidated/e2e-checks.d.ts.map +1 -0
  47. package/dist/checks/consolidated/e2e-checks.js +157 -0
  48. package/dist/checks/consolidated/e2e-checks.js.map +1 -0
  49. package/dist/checks/consolidated/e2e-regression-coverage.d.ts +14 -0
  50. package/dist/checks/consolidated/e2e-regression-coverage.d.ts.map +1 -0
  51. package/dist/checks/consolidated/e2e-regression-coverage.js +151 -0
  52. package/dist/checks/consolidated/e2e-regression-coverage.js.map +1 -0
  53. package/dist/checks/consolidated/e2e-validation.d.ts +137 -0
  54. package/dist/checks/consolidated/e2e-validation.d.ts.map +1 -0
  55. package/dist/checks/consolidated/e2e-validation.js +1001 -0
  56. package/dist/checks/consolidated/e2e-validation.js.map +1 -0
  57. package/dist/checks/consolidated/enterprise-baseline.d.ts +9 -0
  58. package/dist/checks/consolidated/enterprise-baseline.d.ts.map +1 -0
  59. package/dist/checks/consolidated/enterprise-baseline.js +277 -0
  60. package/dist/checks/consolidated/enterprise-baseline.js.map +1 -0
  61. package/dist/checks/consolidated/generate-pageload-config.d.ts +6 -0
  62. package/dist/checks/consolidated/generate-pageload-config.d.ts.map +1 -0
  63. package/dist/checks/consolidated/generate-pageload-config.js +161 -0
  64. package/dist/checks/consolidated/generate-pageload-config.js.map +1 -0
  65. package/dist/checks/consolidated/hardened-checks.d.ts +276 -0
  66. package/dist/checks/consolidated/hardened-checks.d.ts.map +1 -0
  67. package/dist/checks/consolidated/hardened-checks.js +3056 -0
  68. package/dist/checks/consolidated/hardened-checks.js.map +1 -0
  69. package/dist/checks/consolidated/homepage-ux.d.ts +12 -0
  70. package/dist/checks/consolidated/homepage-ux.d.ts.map +1 -0
  71. package/dist/checks/consolidated/homepage-ux.js +242 -0
  72. package/dist/checks/consolidated/homepage-ux.js.map +1 -0
  73. package/dist/checks/consolidated/images.d.ts +76 -0
  74. package/dist/checks/consolidated/images.d.ts.map +1 -0
  75. package/dist/checks/consolidated/images.js +311 -0
  76. package/dist/checks/consolidated/images.js.map +1 -0
  77. package/dist/checks/consolidated/import-cycles.d.ts +63 -0
  78. package/dist/checks/consolidated/import-cycles.d.ts.map +1 -0
  79. package/dist/checks/consolidated/import-cycles.js +291 -0
  80. package/dist/checks/consolidated/import-cycles.js.map +1 -0
  81. package/dist/checks/consolidated/imports.d.ts +112 -0
  82. package/dist/checks/consolidated/imports.d.ts.map +1 -0
  83. package/dist/checks/consolidated/imports.js +977 -0
  84. package/dist/checks/consolidated/imports.js.map +1 -0
  85. package/dist/checks/consolidated/inline-style-conflicts.d.ts +21 -0
  86. package/dist/checks/consolidated/inline-style-conflicts.d.ts.map +1 -0
  87. package/dist/checks/consolidated/inline-style-conflicts.js +300 -0
  88. package/dist/checks/consolidated/inline-style-conflicts.js.map +1 -0
  89. package/dist/checks/consolidated/lib-organization.d.ts +12 -0
  90. package/dist/checks/consolidated/lib-organization.d.ts.map +1 -0
  91. package/dist/checks/consolidated/lib-organization.js +419 -0
  92. package/dist/checks/consolidated/lib-organization.js.map +1 -0
  93. package/dist/checks/consolidated/n-plus-one.d.ts +63 -0
  94. package/dist/checks/consolidated/n-plus-one.d.ts.map +1 -0
  95. package/dist/checks/consolidated/n-plus-one.js +331 -0
  96. package/dist/checks/consolidated/n-plus-one.js.map +1 -0
  97. package/dist/checks/consolidated/nextjs.d.ts +51 -0
  98. package/dist/checks/consolidated/nextjs.d.ts.map +1 -0
  99. package/dist/checks/consolidated/nextjs.js +205 -0
  100. package/dist/checks/consolidated/nextjs.js.map +1 -0
  101. package/dist/checks/consolidated/organization.d.ts +54 -0
  102. package/dist/checks/consolidated/organization.d.ts.map +1 -0
  103. package/dist/checks/consolidated/organization.js +158 -0
  104. package/dist/checks/consolidated/organization.js.map +1 -0
  105. package/dist/checks/consolidated/pageload.d.ts +12 -0
  106. package/dist/checks/consolidated/pageload.d.ts.map +1 -0
  107. package/dist/checks/consolidated/pageload.js +138 -0
  108. package/dist/checks/consolidated/pageload.js.map +1 -0
  109. package/dist/checks/consolidated/performance.d.ts +112 -0
  110. package/dist/checks/consolidated/performance.d.ts.map +1 -0
  111. package/dist/checks/consolidated/performance.js +1546 -0
  112. package/dist/checks/consolidated/performance.js.map +1 -0
  113. package/dist/checks/consolidated/quality.d.ts +52 -0
  114. package/dist/checks/consolidated/quality.d.ts.map +1 -0
  115. package/dist/checks/consolidated/quality.js +253 -0
  116. package/dist/checks/consolidated/quality.js.map +1 -0
  117. package/dist/checks/consolidated/react.d.ts +48 -0
  118. package/dist/checks/consolidated/react.d.ts.map +1 -0
  119. package/dist/checks/consolidated/react.js +203 -0
  120. package/dist/checks/consolidated/react.js.map +1 -0
  121. package/dist/checks/consolidated/regression-hygiene.d.ts +17 -0
  122. package/dist/checks/consolidated/regression-hygiene.d.ts.map +1 -0
  123. package/dist/checks/consolidated/regression-hygiene.js +242 -0
  124. package/dist/checks/consolidated/regression-hygiene.js.map +1 -0
  125. package/dist/checks/consolidated/regression.d.ts +20 -0
  126. package/dist/checks/consolidated/regression.d.ts.map +1 -0
  127. package/dist/checks/consolidated/regression.js +121 -0
  128. package/dist/checks/consolidated/regression.js.map +1 -0
  129. package/dist/checks/consolidated/runtime.d.ts +53 -0
  130. package/dist/checks/consolidated/runtime.d.ts.map +1 -0
  131. package/dist/checks/consolidated/runtime.js +160 -0
  132. package/dist/checks/consolidated/runtime.js.map +1 -0
  133. package/dist/checks/consolidated/script-performance.d.ts +17 -0
  134. package/dist/checks/consolidated/script-performance.d.ts.map +1 -0
  135. package/dist/checks/consolidated/script-performance.js +137 -0
  136. package/dist/checks/consolidated/script-performance.js.map +1 -0
  137. package/dist/checks/consolidated/security.d.ts +78 -0
  138. package/dist/checks/consolidated/security.d.ts.map +1 -0
  139. package/dist/checks/consolidated/security.js +404 -0
  140. package/dist/checks/consolidated/security.js.map +1 -0
  141. package/dist/checks/consolidated/seo.d.ts +31 -0
  142. package/dist/checks/consolidated/seo.d.ts.map +1 -0
  143. package/dist/checks/consolidated/seo.js +1438 -0
  144. package/dist/checks/consolidated/seo.js.map +1 -0
  145. package/dist/checks/consolidated/sx-prop-deprecation.d.ts +22 -0
  146. package/dist/checks/consolidated/sx-prop-deprecation.d.ts.map +1 -0
  147. package/dist/checks/consolidated/sx-prop-deprecation.js +280 -0
  148. package/dist/checks/consolidated/sx-prop-deprecation.js.map +1 -0
  149. package/dist/checks/consolidated/tailwind-class-validation.d.ts +25 -0
  150. package/dist/checks/consolidated/tailwind-class-validation.d.ts.map +1 -0
  151. package/dist/checks/consolidated/tailwind-class-validation.js +533 -0
  152. package/dist/checks/consolidated/tailwind-class-validation.js.map +1 -0
  153. package/dist/checks/consolidated/testing.d.ts +54 -0
  154. package/dist/checks/consolidated/testing.d.ts.map +1 -0
  155. package/dist/checks/consolidated/testing.js +163 -0
  156. package/dist/checks/consolidated/testing.js.map +1 -0
  157. package/dist/checks/consolidated/typescript.d.ts +3 -0
  158. package/dist/checks/consolidated/typescript.d.ts.map +1 -0
  159. package/dist/checks/consolidated/typescript.js +31 -0
  160. package/dist/checks/consolidated/typescript.js.map +1 -0
  161. package/dist/checks/consolidated/ui-accessibility-advanced.d.ts +104 -0
  162. package/dist/checks/consolidated/ui-accessibility-advanced.d.ts.map +1 -0
  163. package/dist/checks/consolidated/ui-accessibility-advanced.js +689 -0
  164. package/dist/checks/consolidated/ui-accessibility-advanced.js.map +1 -0
  165. package/dist/checks/consolidated/ui-accessibility.d.ts +121 -0
  166. package/dist/checks/consolidated/ui-accessibility.d.ts.map +1 -0
  167. package/dist/checks/consolidated/ui-accessibility.js +776 -0
  168. package/dist/checks/consolidated/ui-accessibility.js.map +1 -0
  169. package/dist/checks/consolidated/ui-advanced-spacing.d.ts +142 -0
  170. package/dist/checks/consolidated/ui-advanced-spacing.d.ts.map +1 -0
  171. package/dist/checks/consolidated/ui-advanced-spacing.js +1220 -0
  172. package/dist/checks/consolidated/ui-advanced-spacing.js.map +1 -0
  173. package/dist/checks/consolidated/ui-animation-duration.d.ts +108 -0
  174. package/dist/checks/consolidated/ui-animation-duration.d.ts.map +1 -0
  175. package/dist/checks/consolidated/ui-animation-duration.js +531 -0
  176. package/dist/checks/consolidated/ui-animation-duration.js.map +1 -0
  177. package/dist/checks/consolidated/ui-border-radius.d.ts +90 -0
  178. package/dist/checks/consolidated/ui-border-radius.d.ts.map +1 -0
  179. package/dist/checks/consolidated/ui-border-radius.js +519 -0
  180. package/dist/checks/consolidated/ui-border-radius.js.map +1 -0
  181. package/dist/checks/consolidated/ui-buttons.d.ts +32 -0
  182. package/dist/checks/consolidated/ui-buttons.d.ts.map +1 -0
  183. package/dist/checks/consolidated/ui-buttons.js +481 -0
  184. package/dist/checks/consolidated/ui-buttons.js.map +1 -0
  185. package/dist/checks/consolidated/ui-cards.d.ts +29 -0
  186. package/dist/checks/consolidated/ui-cards.d.ts.map +1 -0
  187. package/dist/checks/consolidated/ui-cards.js +504 -0
  188. package/dist/checks/consolidated/ui-cards.js.map +1 -0
  189. package/dist/checks/consolidated/ui-checks.d.ts +48 -0
  190. package/dist/checks/consolidated/ui-checks.d.ts.map +1 -0
  191. package/dist/checks/consolidated/ui-checks.js +264 -0
  192. package/dist/checks/consolidated/ui-checks.js.map +1 -0
  193. package/dist/checks/consolidated/ui-cleanup.d.ts +81 -0
  194. package/dist/checks/consolidated/ui-cleanup.d.ts.map +1 -0
  195. package/dist/checks/consolidated/ui-cleanup.js +650 -0
  196. package/dist/checks/consolidated/ui-cleanup.js.map +1 -0
  197. package/dist/checks/consolidated/ui-components.d.ts +255 -0
  198. package/dist/checks/consolidated/ui-components.d.ts.map +1 -0
  199. package/dist/checks/consolidated/ui-components.js +2008 -0
  200. package/dist/checks/consolidated/ui-components.js.map +1 -0
  201. package/dist/checks/consolidated/ui-consistency-advanced.d.ts +130 -0
  202. package/dist/checks/consolidated/ui-consistency-advanced.d.ts.map +1 -0
  203. package/dist/checks/consolidated/ui-consistency-advanced.js +982 -0
  204. package/dist/checks/consolidated/ui-consistency-advanced.js.map +1 -0
  205. package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts +30 -0
  206. package/dist/checks/consolidated/ui-consistency-comprehensive.d.ts.map +1 -0
  207. package/dist/checks/consolidated/ui-consistency-comprehensive.js +1018 -0
  208. package/dist/checks/consolidated/ui-consistency-comprehensive.js.map +1 -0
  209. package/dist/checks/consolidated/ui-consistency-extended.d.ts +26 -0
  210. package/dist/checks/consolidated/ui-consistency-extended.d.ts.map +1 -0
  211. package/dist/checks/consolidated/ui-consistency-extended.js +606 -0
  212. package/dist/checks/consolidated/ui-consistency-extended.js.map +1 -0
  213. package/dist/checks/consolidated/ui-data-display.d.ts +103 -0
  214. package/dist/checks/consolidated/ui-data-display.d.ts.map +1 -0
  215. package/dist/checks/consolidated/ui-data-display.js +740 -0
  216. package/dist/checks/consolidated/ui-data-display.js.map +1 -0
  217. package/dist/checks/consolidated/ui-deprecated.d.ts +22 -0
  218. package/dist/checks/consolidated/ui-deprecated.d.ts.map +1 -0
  219. package/dist/checks/consolidated/ui-deprecated.js +336 -0
  220. package/dist/checks/consolidated/ui-deprecated.js.map +1 -0
  221. package/dist/checks/consolidated/ui-empty-null-states.d.ts +90 -0
  222. package/dist/checks/consolidated/ui-empty-null-states.d.ts.map +1 -0
  223. package/dist/checks/consolidated/ui-empty-null-states.js +511 -0
  224. package/dist/checks/consolidated/ui-empty-null-states.js.map +1 -0
  225. package/dist/checks/consolidated/ui-error-states.d.ts +99 -0
  226. package/dist/checks/consolidated/ui-error-states.d.ts.map +1 -0
  227. package/dist/checks/consolidated/ui-error-states.js +694 -0
  228. package/dist/checks/consolidated/ui-error-states.js.map +1 -0
  229. package/dist/checks/consolidated/ui-feedback-confirmations.d.ts +90 -0
  230. package/dist/checks/consolidated/ui-feedback-confirmations.d.ts.map +1 -0
  231. package/dist/checks/consolidated/ui-feedback-confirmations.js +596 -0
  232. package/dist/checks/consolidated/ui-feedback-confirmations.js.map +1 -0
  233. package/dist/checks/consolidated/ui-forms.d.ts +32 -0
  234. package/dist/checks/consolidated/ui-forms.d.ts.map +1 -0
  235. package/dist/checks/consolidated/ui-forms.js +568 -0
  236. package/dist/checks/consolidated/ui-forms.js.map +1 -0
  237. package/dist/checks/consolidated/ui-gradient-shadow.d.ts +90 -0
  238. package/dist/checks/consolidated/ui-gradient-shadow.d.ts.map +1 -0
  239. package/dist/checks/consolidated/ui-gradient-shadow.js +568 -0
  240. package/dist/checks/consolidated/ui-gradient-shadow.js.map +1 -0
  241. package/dist/checks/consolidated/ui-grid-responsive.d.ts +27 -0
  242. package/dist/checks/consolidated/ui-grid-responsive.d.ts.map +1 -0
  243. package/dist/checks/consolidated/ui-grid-responsive.js +441 -0
  244. package/dist/checks/consolidated/ui-grid-responsive.js.map +1 -0
  245. package/dist/checks/consolidated/ui-icon-size-tokens.d.ts +104 -0
  246. package/dist/checks/consolidated/ui-icon-size-tokens.d.ts.map +1 -0
  247. package/dist/checks/consolidated/ui-icon-size-tokens.js +514 -0
  248. package/dist/checks/consolidated/ui-icon-size-tokens.js.map +1 -0
  249. package/dist/checks/consolidated/ui-iconography.d.ts +90 -0
  250. package/dist/checks/consolidated/ui-iconography.d.ts.map +1 -0
  251. package/dist/checks/consolidated/ui-iconography.js +565 -0
  252. package/dist/checks/consolidated/ui-iconography.js.map +1 -0
  253. package/dist/checks/consolidated/ui-interactive-states.d.ts +240 -0
  254. package/dist/checks/consolidated/ui-interactive-states.d.ts.map +1 -0
  255. package/dist/checks/consolidated/ui-interactive-states.js +2474 -0
  256. package/dist/checks/consolidated/ui-interactive-states.js.map +1 -0
  257. package/dist/checks/consolidated/ui-layout.d.ts +256 -0
  258. package/dist/checks/consolidated/ui-layout.d.ts.map +1 -0
  259. package/dist/checks/consolidated/ui-layout.js +1371 -0
  260. package/dist/checks/consolidated/ui-layout.js.map +1 -0
  261. package/dist/checks/consolidated/ui-loading-skeletons.d.ts +11 -0
  262. package/dist/checks/consolidated/ui-loading-skeletons.d.ts.map +1 -0
  263. package/dist/checks/consolidated/ui-loading-skeletons.js +145 -0
  264. package/dist/checks/consolidated/ui-loading-skeletons.js.map +1 -0
  265. package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts +9 -0
  266. package/dist/checks/consolidated/ui-loading-state-skeletons.d.ts.map +1 -0
  267. package/dist/checks/consolidated/ui-loading-state-skeletons.js +125 -0
  268. package/dist/checks/consolidated/ui-loading-state-skeletons.js.map +1 -0
  269. package/dist/checks/consolidated/ui-media.d.ts +74 -0
  270. package/dist/checks/consolidated/ui-media.d.ts.map +1 -0
  271. package/dist/checks/consolidated/ui-media.js +408 -0
  272. package/dist/checks/consolidated/ui-media.js.map +1 -0
  273. package/dist/checks/consolidated/ui-micro-interactions.d.ts +107 -0
  274. package/dist/checks/consolidated/ui-micro-interactions.d.ts.map +1 -0
  275. package/dist/checks/consolidated/ui-micro-interactions.js +825 -0
  276. package/dist/checks/consolidated/ui-micro-interactions.js.map +1 -0
  277. package/dist/checks/consolidated/ui-microcopy-consistency.d.ts +114 -0
  278. package/dist/checks/consolidated/ui-microcopy-consistency.d.ts.map +1 -0
  279. package/dist/checks/consolidated/ui-microcopy-consistency.js +566 -0
  280. package/dist/checks/consolidated/ui-microcopy-consistency.js.map +1 -0
  281. package/dist/checks/consolidated/ui-mobile-ux.d.ts +251 -0
  282. package/dist/checks/consolidated/ui-mobile-ux.d.ts.map +1 -0
  283. package/dist/checks/consolidated/ui-mobile-ux.js +2212 -0
  284. package/dist/checks/consolidated/ui-mobile-ux.js.map +1 -0
  285. package/dist/checks/consolidated/ui-motion-accessibility.d.ts +93 -0
  286. package/dist/checks/consolidated/ui-motion-accessibility.d.ts.map +1 -0
  287. package/dist/checks/consolidated/ui-motion-accessibility.js +450 -0
  288. package/dist/checks/consolidated/ui-motion-accessibility.js.map +1 -0
  289. package/dist/checks/consolidated/ui-navigation.d.ts +85 -0
  290. package/dist/checks/consolidated/ui-navigation.d.ts.map +1 -0
  291. package/dist/checks/consolidated/ui-navigation.js +673 -0
  292. package/dist/checks/consolidated/ui-navigation.js.map +1 -0
  293. package/dist/checks/consolidated/ui-patterns.d.ts +174 -0
  294. package/dist/checks/consolidated/ui-patterns.d.ts.map +1 -0
  295. package/dist/checks/consolidated/ui-patterns.js +1532 -0
  296. package/dist/checks/consolidated/ui-patterns.js.map +1 -0
  297. package/dist/checks/consolidated/ui-responsive.d.ts +89 -0
  298. package/dist/checks/consolidated/ui-responsive.d.ts.map +1 -0
  299. package/dist/checks/consolidated/ui-responsive.js +588 -0
  300. package/dist/checks/consolidated/ui-responsive.js.map +1 -0
  301. package/dist/checks/consolidated/ui-spacing-standards.d.ts +43 -0
  302. package/dist/checks/consolidated/ui-spacing-standards.d.ts.map +1 -0
  303. package/dist/checks/consolidated/ui-spacing-standards.js +874 -0
  304. package/dist/checks/consolidated/ui-spacing-standards.js.map +1 -0
  305. package/dist/checks/consolidated/ui-spacing.d.ts +751 -0
  306. package/dist/checks/consolidated/ui-spacing.d.ts.map +1 -0
  307. package/dist/checks/consolidated/ui-spacing.js +4996 -0
  308. package/dist/checks/consolidated/ui-spacing.js.map +1 -0
  309. package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts +70 -0
  310. package/dist/checks/consolidated/ui-standards-auto-fixer.d.ts.map +1 -0
  311. package/dist/checks/consolidated/ui-standards-auto-fixer.js +429 -0
  312. package/dist/checks/consolidated/ui-standards-auto-fixer.js.map +1 -0
  313. package/dist/checks/consolidated/ui-standards-enforcement.d.ts +100 -0
  314. package/dist/checks/consolidated/ui-standards-enforcement.d.ts.map +1 -0
  315. package/dist/checks/consolidated/ui-standards-enforcement.js +935 -0
  316. package/dist/checks/consolidated/ui-standards-enforcement.js.map +1 -0
  317. package/dist/checks/consolidated/ui-state-consistency.d.ts +90 -0
  318. package/dist/checks/consolidated/ui-state-consistency.d.ts.map +1 -0
  319. package/dist/checks/consolidated/ui-state-consistency.js +659 -0
  320. package/dist/checks/consolidated/ui-state-consistency.js.map +1 -0
  321. package/dist/checks/consolidated/ui-style-validation.d.ts +74 -0
  322. package/dist/checks/consolidated/ui-style-validation.d.ts.map +1 -0
  323. package/dist/checks/consolidated/ui-style-validation.js +403 -0
  324. package/dist/checks/consolidated/ui-style-validation.js.map +1 -0
  325. package/dist/checks/consolidated/ui-tokens.d.ts +110 -0
  326. package/dist/checks/consolidated/ui-tokens.d.ts.map +1 -0
  327. package/dist/checks/consolidated/ui-tokens.js +990 -0
  328. package/dist/checks/consolidated/ui-tokens.js.map +1 -0
  329. package/dist/checks/consolidated/ui-typography.d.ts +77 -0
  330. package/dist/checks/consolidated/ui-typography.d.ts.map +1 -0
  331. package/dist/checks/consolidated/ui-typography.js +416 -0
  332. package/dist/checks/consolidated/ui-typography.js.map +1 -0
  333. package/dist/checks/consolidated/ui-visual-hierarchy.d.ts +90 -0
  334. package/dist/checks/consolidated/ui-visual-hierarchy.d.ts.map +1 -0
  335. package/dist/checks/consolidated/ui-visual-hierarchy.js +562 -0
  336. package/dist/checks/consolidated/ui-visual-hierarchy.js.map +1 -0
  337. package/dist/checks/consolidated/woocommerce.d.ts +50 -0
  338. package/dist/checks/consolidated/woocommerce.d.ts.map +1 -0
  339. package/dist/checks/consolidated/woocommerce.js +198 -0
  340. package/dist/checks/consolidated/woocommerce.js.map +1 -0
  341. package/dist/checks/core/api-route-protection.d.ts +2 -0
  342. package/dist/checks/core/api-route-protection.d.ts.map +1 -0
  343. package/dist/checks/core/api-route-protection.js +101 -0
  344. package/dist/checks/core/api-route-protection.js.map +1 -0
  345. package/dist/checks/core/critical.d.ts +8 -0
  346. package/dist/checks/core/critical.d.ts.map +1 -0
  347. package/dist/checks/core/critical.js +200 -0
  348. package/dist/checks/core/critical.js.map +1 -0
  349. package/dist/checks/core/database.d.ts +8 -0
  350. package/dist/checks/core/database.d.ts.map +1 -0
  351. package/dist/checks/core/database.js +699 -0
  352. package/dist/checks/core/database.js.map +1 -0
  353. package/dist/checks/core/development.d.ts +8 -0
  354. package/dist/checks/core/development.d.ts.map +1 -0
  355. package/dist/checks/core/development.js +417 -0
  356. package/dist/checks/core/development.js.map +1 -0
  357. package/dist/checks/core/hydration-mismatch-check.d.ts +38 -0
  358. package/dist/checks/core/hydration-mismatch-check.d.ts.map +1 -0
  359. package/dist/checks/core/hydration-mismatch-check.js +411 -0
  360. package/dist/checks/core/hydration-mismatch-check.js.map +1 -0
  361. package/dist/checks/core/performance.d.ts +8 -0
  362. package/dist/checks/core/performance.d.ts.map +1 -0
  363. package/dist/checks/core/performance.js +474 -0
  364. package/dist/checks/core/performance.js.map +1 -0
  365. package/dist/checks/core/security.d.ts +8 -0
  366. package/dist/checks/core/security.d.ts.map +1 -0
  367. package/dist/checks/core/security.js +275 -0
  368. package/dist/checks/core/security.js.map +1 -0
  369. package/dist/checks/core/standardized-error-handling.d.ts +43 -0
  370. package/dist/checks/core/standardized-error-handling.d.ts.map +1 -0
  371. package/dist/checks/core/standardized-error-handling.js +384 -0
  372. package/dist/checks/core/standardized-error-handling.js.map +1 -0
  373. package/dist/checks/core/supercatch.d.ts +8 -0
  374. package/dist/checks/core/supercatch.d.ts.map +1 -0
  375. package/dist/checks/core/supercatch.js +750 -0
  376. package/dist/checks/core/supercatch.js.map +1 -0
  377. package/dist/checks/core/suppression-check.d.ts +2 -0
  378. package/dist/checks/core/suppression-check.d.ts.map +1 -0
  379. package/dist/checks/core/suppression-check.js +129 -0
  380. package/dist/checks/core/suppression-check.js.map +1 -0
  381. package/dist/checks/core/ui-quality.d.ts +8 -0
  382. package/dist/checks/core/ui-quality.d.ts.map +1 -0
  383. package/dist/checks/core/ui-quality.js +1736 -0
  384. package/dist/checks/core/ui-quality.js.map +1 -0
  385. package/dist/checks/core/unused-assets-check.d.ts +2 -0
  386. package/dist/checks/core/unused-assets-check.d.ts.map +1 -0
  387. package/dist/checks/core/unused-assets-check.js +112 -0
  388. package/dist/checks/core/unused-assets-check.js.map +1 -0
  389. package/dist/checks/core/use-status-ssr-safety.d.ts +34 -0
  390. package/dist/checks/core/use-status-ssr-safety.d.ts.map +1 -0
  391. package/dist/checks/core/use-status-ssr-safety.js +283 -0
  392. package/dist/checks/core/use-status-ssr-safety.js.map +1 -0
  393. package/dist/checks/email/email-flow-validation.d.ts +23 -0
  394. package/dist/checks/email/email-flow-validation.d.ts.map +1 -0
  395. package/dist/checks/email/email-flow-validation.js +468 -0
  396. package/dist/checks/email/email-flow-validation.js.map +1 -0
  397. package/dist/checks/email/email-template-db-verification.d.ts +20 -0
  398. package/dist/checks/email/email-template-db-verification.d.ts.map +1 -0
  399. package/dist/checks/email/email-template-db-verification.js +46 -0
  400. package/dist/checks/email/email-template-db-verification.js.map +1 -0
  401. package/dist/checks/email/email-template-validation.d.ts +24 -0
  402. package/dist/checks/email/email-template-validation.d.ts.map +1 -0
  403. package/dist/checks/email/email-template-validation.js +688 -0
  404. package/dist/checks/email/email-template-validation.js.map +1 -0
  405. package/dist/checks/jsx/comment-placement.d.ts +45 -0
  406. package/dist/checks/jsx/comment-placement.d.ts.map +1 -0
  407. package/dist/checks/jsx/comment-placement.js +316 -0
  408. package/dist/checks/jsx/comment-placement.js.map +1 -0
  409. package/dist/checks/specialized/admin-layout-check.d.ts +19 -0
  410. package/dist/checks/specialized/admin-layout-check.d.ts.map +1 -0
  411. package/dist/checks/specialized/admin-layout-check.js +166 -0
  412. package/dist/checks/specialized/admin-layout-check.js.map +1 -0
  413. package/dist/checks/specialized/client-server-separation.d.ts +14 -0
  414. package/dist/checks/specialized/client-server-separation.d.ts.map +1 -0
  415. package/dist/checks/specialized/client-server-separation.js +197 -0
  416. package/dist/checks/specialized/client-server-separation.js.map +1 -0
  417. package/dist/checks/specialized/cost-optimization.d.ts +18 -0
  418. package/dist/checks/specialized/cost-optimization.d.ts.map +1 -0
  419. package/dist/checks/specialized/cost-optimization.js +78 -0
  420. package/dist/checks/specialized/cost-optimization.js.map +1 -0
  421. package/dist/checks/specialized/database-migration-sync.d.ts +21 -0
  422. package/dist/checks/specialized/database-migration-sync.d.ts.map +1 -0
  423. package/dist/checks/specialized/database-migration-sync.js +150 -0
  424. package/dist/checks/specialized/database-migration-sync.js.map +1 -0
  425. package/dist/checks/specialized/database-model-validation.d.ts +15 -0
  426. package/dist/checks/specialized/database-model-validation.d.ts.map +1 -0
  427. package/dist/checks/specialized/database-model-validation.js +35 -0
  428. package/dist/checks/specialized/database-model-validation.js.map +1 -0
  429. package/dist/checks/specialized/database-schema-migrations-diff.d.ts +27 -0
  430. package/dist/checks/specialized/database-schema-migrations-diff.d.ts.map +1 -0
  431. package/dist/checks/specialized/database-schema-migrations-diff.js +177 -0
  432. package/dist/checks/specialized/database-schema-migrations-diff.js.map +1 -0
  433. package/dist/checks/specialized/database-schema-sync.d.ts +23 -0
  434. package/dist/checks/specialized/database-schema-sync.d.ts.map +1 -0
  435. package/dist/checks/specialized/database-schema-sync.js +77 -0
  436. package/dist/checks/specialized/database-schema-sync.js.map +1 -0
  437. package/dist/checks/specialized/decimal-serialization.d.ts +24 -0
  438. package/dist/checks/specialized/decimal-serialization.d.ts.map +1 -0
  439. package/dist/checks/specialized/decimal-serialization.js +400 -0
  440. package/dist/checks/specialized/decimal-serialization.js.map +1 -0
  441. package/dist/checks/specialized/detect-router-issues.d.ts +14 -0
  442. package/dist/checks/specialized/detect-router-issues.d.ts.map +1 -0
  443. package/dist/checks/specialized/detect-router-issues.js +96 -0
  444. package/dist/checks/specialized/detect-router-issues.js.map +1 -0
  445. package/dist/checks/specialized/enum-validation.d.ts +15 -0
  446. package/dist/checks/specialized/enum-validation.d.ts.map +1 -0
  447. package/dist/checks/specialized/enum-validation.js +35 -0
  448. package/dist/checks/specialized/enum-validation.js.map +1 -0
  449. package/dist/checks/specialized/hash-collision.d.ts +18 -0
  450. package/dist/checks/specialized/hash-collision.d.ts.map +1 -0
  451. package/dist/checks/specialized/hash-collision.js +78 -0
  452. package/dist/checks/specialized/hash-collision.js.map +1 -0
  453. package/dist/checks/specialized/id-generation-enforcement.d.ts +16 -0
  454. package/dist/checks/specialized/id-generation-enforcement.d.ts.map +1 -0
  455. package/dist/checks/specialized/id-generation-enforcement.js +307 -0
  456. package/dist/checks/specialized/id-generation-enforcement.js.map +1 -0
  457. package/dist/checks/specialized/image-data-integrity.d.ts +15 -0
  458. package/dist/checks/specialized/image-data-integrity.d.ts.map +1 -0
  459. package/dist/checks/specialized/image-data-integrity.js +79 -0
  460. package/dist/checks/specialized/image-data-integrity.js.map +1 -0
  461. package/dist/checks/specialized/image-health.d.ts +14 -0
  462. package/dist/checks/specialized/image-health.d.ts.map +1 -0
  463. package/dist/checks/specialized/image-health.js +122 -0
  464. package/dist/checks/specialized/image-health.js.map +1 -0
  465. package/dist/checks/specialized/image-metadata-validation.d.ts +14 -0
  466. package/dist/checks/specialized/image-metadata-validation.d.ts.map +1 -0
  467. package/dist/checks/specialized/image-metadata-validation.js +95 -0
  468. package/dist/checks/specialized/image-metadata-validation.js.map +1 -0
  469. package/dist/checks/specialized/image-optimization.d.ts +16 -0
  470. package/dist/checks/specialized/image-optimization.d.ts.map +1 -0
  471. package/dist/checks/specialized/image-optimization.js +86 -0
  472. package/dist/checks/specialized/image-optimization.js.map +1 -0
  473. package/dist/checks/specialized/invalid-module-imports.d.ts +24 -0
  474. package/dist/checks/specialized/invalid-module-imports.d.ts.map +1 -0
  475. package/dist/checks/specialized/invalid-module-imports.js +209 -0
  476. package/dist/checks/specialized/invalid-module-imports.js.map +1 -0
  477. package/dist/checks/specialized/lint-validation.d.ts +26 -0
  478. package/dist/checks/specialized/lint-validation.d.ts.map +1 -0
  479. package/dist/checks/specialized/lint-validation.js +193 -0
  480. package/dist/checks/specialized/lint-validation.js.map +1 -0
  481. package/dist/checks/specialized/listing-workflow.d.ts +19 -0
  482. package/dist/checks/specialized/listing-workflow.d.ts.map +1 -0
  483. package/dist/checks/specialized/listing-workflow.js +89 -0
  484. package/dist/checks/specialized/listing-workflow.js.map +1 -0
  485. package/dist/checks/specialized/mui-imports-validation.d.ts +18 -0
  486. package/dist/checks/specialized/mui-imports-validation.d.ts.map +1 -0
  487. package/dist/checks/specialized/mui-imports-validation.js +134 -0
  488. package/dist/checks/specialized/mui-imports-validation.js.map +1 -0
  489. package/dist/checks/specialized/nextauth-v5-compliance.d.ts +16 -0
  490. package/dist/checks/specialized/nextauth-v5-compliance.d.ts.map +1 -0
  491. package/dist/checks/specialized/nextauth-v5-compliance.js +164 -0
  492. package/dist/checks/specialized/nextauth-v5-compliance.js.map +1 -0
  493. package/dist/checks/specialized/nextjs-params-check.d.ts +14 -0
  494. package/dist/checks/specialized/nextjs-params-check.d.ts.map +1 -0
  495. package/dist/checks/specialized/nextjs-params-check.js +140 -0
  496. package/dist/checks/specialized/nextjs-params-check.js.map +1 -0
  497. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts +16 -0
  498. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.d.ts.map +1 -0
  499. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js +36 -0
  500. package/dist/checks/specialized/no-legacy-catalog-aliases-validation.js.map +1 -0
  501. package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts +22 -0
  502. package/dist/checks/specialized/no-wata-cardgraded-validation.d.ts.map +1 -0
  503. package/dist/checks/specialized/no-wata-cardgraded-validation.js +97 -0
  504. package/dist/checks/specialized/no-wata-cardgraded-validation.js.map +1 -0
  505. package/dist/checks/specialized/parameter-consistency-check.d.ts +20 -0
  506. package/dist/checks/specialized/parameter-consistency-check.d.ts.map +1 -0
  507. package/dist/checks/specialized/parameter-consistency-check.js +115 -0
  508. package/dist/checks/specialized/parameter-consistency-check.js.map +1 -0
  509. package/dist/checks/specialized/prisma-field-names-validation.d.ts +15 -0
  510. package/dist/checks/specialized/prisma-field-names-validation.d.ts.map +1 -0
  511. package/dist/checks/specialized/prisma-field-names-validation.js +35 -0
  512. package/dist/checks/specialized/prisma-field-names-validation.js.map +1 -0
  513. package/dist/checks/specialized/prisma-null-syntax.d.ts +34 -0
  514. package/dist/checks/specialized/prisma-null-syntax.d.ts.map +1 -0
  515. package/dist/checks/specialized/prisma-null-syntax.js +330 -0
  516. package/dist/checks/specialized/prisma-null-syntax.js.map +1 -0
  517. package/dist/checks/specialized/prisma-query-validation.d.ts +15 -0
  518. package/dist/checks/specialized/prisma-query-validation.d.ts.map +1 -0
  519. package/dist/checks/specialized/prisma-query-validation.js +35 -0
  520. package/dist/checks/specialized/prisma-query-validation.js.map +1 -0
  521. package/dist/checks/specialized/product-type-validation.d.ts +17 -0
  522. package/dist/checks/specialized/product-type-validation.d.ts.map +1 -0
  523. package/dist/checks/specialized/product-type-validation.js +129 -0
  524. package/dist/checks/specialized/product-type-validation.js.map +1 -0
  525. package/dist/checks/specialized/responsive-image-validation.d.ts +14 -0
  526. package/dist/checks/specialized/responsive-image-validation.d.ts.map +1 -0
  527. package/dist/checks/specialized/responsive-image-validation.js +101 -0
  528. package/dist/checks/specialized/responsive-image-validation.js.map +1 -0
  529. package/dist/checks/specialized/root-cleanliness.d.ts +21 -0
  530. package/dist/checks/specialized/root-cleanliness.d.ts.map +1 -0
  531. package/dist/checks/specialized/root-cleanliness.js +251 -0
  532. package/dist/checks/specialized/root-cleanliness.js.map +1 -0
  533. package/dist/checks/specialized/rotation-detection-validation.d.ts +16 -0
  534. package/dist/checks/specialized/rotation-detection-validation.d.ts.map +1 -0
  535. package/dist/checks/specialized/rotation-detection-validation.js +113 -0
  536. package/dist/checks/specialized/rotation-detection-validation.js.map +1 -0
  537. package/dist/checks/specialized/script-organization.d.ts +17 -0
  538. package/dist/checks/specialized/script-organization.d.ts.map +1 -0
  539. package/dist/checks/specialized/script-organization.js +487 -0
  540. package/dist/checks/specialized/script-organization.js.map +1 -0
  541. package/dist/checks/specialized/shared-components-migration.d.ts +137 -0
  542. package/dist/checks/specialized/shared-components-migration.d.ts.map +1 -0
  543. package/dist/checks/specialized/shared-components-migration.js +1288 -0
  544. package/dist/checks/specialized/shared-components-migration.js.map +1 -0
  545. package/dist/checks/specialized/store-specialties-normalization.d.ts +10 -0
  546. package/dist/checks/specialized/store-specialties-normalization.d.ts.map +1 -0
  547. package/dist/checks/specialized/store-specialties-normalization.js +126 -0
  548. package/dist/checks/specialized/store-specialties-normalization.js.map +1 -0
  549. package/dist/checks/specialized/two-stage-trim-validation.d.ts +16 -0
  550. package/dist/checks/specialized/two-stage-trim-validation.d.ts.map +1 -0
  551. package/dist/checks/specialized/two-stage-trim-validation.js +115 -0
  552. package/dist/checks/specialized/two-stage-trim-validation.js.map +1 -0
  553. package/dist/checks/specialized/underscore-variable-audit.d.ts +26 -0
  554. package/dist/checks/specialized/underscore-variable-audit.d.ts.map +1 -0
  555. package/dist/checks/specialized/underscore-variable-audit.js +219 -0
  556. package/dist/checks/specialized/underscore-variable-audit.js.map +1 -0
  557. package/dist/checks/specialized/unified-badge-consistency.d.ts +16 -0
  558. package/dist/checks/specialized/unified-badge-consistency.d.ts.map +1 -0
  559. package/dist/checks/specialized/unified-badge-consistency.js +284 -0
  560. package/dist/checks/specialized/unified-badge-consistency.js.map +1 -0
  561. package/dist/checks/specialized/validate-integration-enums.d.ts +15 -0
  562. package/dist/checks/specialized/validate-integration-enums.d.ts.map +1 -0
  563. package/dist/checks/specialized/validate-integration-enums.js +131 -0
  564. package/dist/checks/specialized/validate-integration-enums.js.map +1 -0
  565. package/dist/checks/testing/action-regression.d.ts +23 -0
  566. package/dist/checks/testing/action-regression.d.ts.map +1 -0
  567. package/dist/checks/testing/action-regression.js +192 -0
  568. package/dist/checks/testing/action-regression.js.map +1 -0
  569. package/dist/checks/testing/critical-api-coverage.d.ts +21 -0
  570. package/dist/checks/testing/critical-api-coverage.d.ts.map +1 -0
  571. package/dist/checks/testing/critical-api-coverage.js +158 -0
  572. package/dist/checks/testing/critical-api-coverage.js.map +1 -0
  573. package/dist/checks/testing/data-entry-regression-required.d.ts +24 -0
  574. package/dist/checks/testing/data-entry-regression-required.d.ts.map +1 -0
  575. package/dist/checks/testing/data-entry-regression-required.js +378 -0
  576. package/dist/checks/testing/data-entry-regression-required.js.map +1 -0
  577. package/dist/checks/testing/e2e-best-practices.d.ts +24 -0
  578. package/dist/checks/testing/e2e-best-practices.d.ts.map +1 -0
  579. package/dist/checks/testing/e2e-best-practices.js +791 -0
  580. package/dist/checks/testing/e2e-best-practices.js.map +1 -0
  581. package/dist/checks/testing/e2e-flake-patterns.d.ts +26 -0
  582. package/dist/checks/testing/e2e-flake-patterns.d.ts.map +1 -0
  583. package/dist/checks/testing/e2e-flake-patterns.js +305 -0
  584. package/dist/checks/testing/e2e-flake-patterns.js.map +1 -0
  585. package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts +25 -0
  586. package/dist/checks/testing/e2e-redundant-visibility-checks.d.ts.map +1 -0
  587. package/dist/checks/testing/e2e-redundant-visibility-checks.js +613 -0
  588. package/dist/checks/testing/e2e-redundant-visibility-checks.js.map +1 -0
  589. package/dist/checks/testing/e2e-slow-tests.d.ts +9 -0
  590. package/dist/checks/testing/e2e-slow-tests.d.ts.map +1 -0
  591. package/dist/checks/testing/e2e-slow-tests.js +142 -0
  592. package/dist/checks/testing/e2e-slow-tests.js.map +1 -0
  593. package/dist/checks/testing/e2e-timeouts.d.ts +9 -0
  594. package/dist/checks/testing/e2e-timeouts.d.ts.map +1 -0
  595. package/dist/checks/testing/e2e-timeouts.js +82 -0
  596. package/dist/checks/testing/e2e-timeouts.js.map +1 -0
  597. package/dist/checks/testing/integration-e2e-depth.d.ts +20 -0
  598. package/dist/checks/testing/integration-e2e-depth.d.ts.map +1 -0
  599. package/dist/checks/testing/integration-e2e-depth.js +575 -0
  600. package/dist/checks/testing/integration-e2e-depth.js.map +1 -0
  601. package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts +31 -0
  602. package/dist/checks/testing/playwright-feature-coverage-gaps.d.ts.map +1 -0
  603. package/dist/checks/testing/playwright-feature-coverage-gaps.js +1582 -0
  604. package/dist/checks/testing/playwright-feature-coverage-gaps.js.map +1 -0
  605. package/dist/checks/testing/playwright-mock-inventory.d.ts +24 -0
  606. package/dist/checks/testing/playwright-mock-inventory.d.ts.map +1 -0
  607. package/dist/checks/testing/playwright-mock-inventory.js +380 -0
  608. package/dist/checks/testing/playwright-mock-inventory.js.map +1 -0
  609. package/dist/checks/testing/test-coverage-threshold.d.ts +25 -0
  610. package/dist/checks/testing/test-coverage-threshold.d.ts.map +1 -0
  611. package/dist/checks/testing/test-coverage-threshold.js +166 -0
  612. package/dist/checks/testing/test-coverage-threshold.js.map +1 -0
  613. package/dist/checks/testing/test-flakiness-score.d.ts +27 -0
  614. package/dist/checks/testing/test-flakiness-score.d.ts.map +1 -0
  615. package/dist/checks/testing/test-flakiness-score.js +358 -0
  616. package/dist/checks/testing/test-flakiness-score.js.map +1 -0
  617. package/dist/checks/testing/test-patterns.d.ts +16 -0
  618. package/dist/checks/testing/test-patterns.d.ts.map +1 -0
  619. package/dist/checks/testing/test-patterns.js +156 -0
  620. package/dist/checks/testing/test-patterns.js.map +1 -0
  621. package/dist/checks/workflows/a-plus-rating-validation.d.ts +42 -0
  622. package/dist/checks/workflows/a-plus-rating-validation.d.ts.map +1 -0
  623. package/dist/checks/workflows/a-plus-rating-validation.js +527 -0
  624. package/dist/checks/workflows/a-plus-rating-validation.js.map +1 -0
  625. package/dist/checks/workflows/affected.d.ts +14 -0
  626. package/dist/checks/workflows/affected.d.ts.map +1 -0
  627. package/dist/checks/workflows/affected.js +126 -0
  628. package/dist/checks/workflows/affected.js.map +1 -0
  629. package/dist/checks/workflows/ai.d.ts +6 -0
  630. package/dist/checks/workflows/ai.d.ts.map +1 -0
  631. package/dist/checks/workflows/ai.js +42 -0
  632. package/dist/checks/workflows/ai.js.map +1 -0
  633. package/dist/checks/workflows/all.d.ts +31 -0
  634. package/dist/checks/workflows/all.d.ts.map +1 -0
  635. package/dist/checks/workflows/all.js +2688 -0
  636. package/dist/checks/workflows/all.js.map +1 -0
  637. package/dist/checks/workflows/commit.d.ts +19 -0
  638. package/dist/checks/workflows/commit.d.ts.map +1 -0
  639. package/dist/checks/workflows/commit.js +207 -0
  640. package/dist/checks/workflows/commit.js.map +1 -0
  641. package/dist/checks/workflows/critical.d.ts +9 -0
  642. package/dist/checks/workflows/critical.d.ts.map +1 -0
  643. package/dist/checks/workflows/critical.js +213 -0
  644. package/dist/checks/workflows/critical.js.map +1 -0
  645. package/dist/checks/workflows/database-id-validation.d.ts +9 -0
  646. package/dist/checks/workflows/database-id-validation.d.ts.map +1 -0
  647. package/dist/checks/workflows/database-id-validation.js +13 -0
  648. package/dist/checks/workflows/database-id-validation.js.map +1 -0
  649. package/dist/checks/workflows/deploy.d.ts +20 -0
  650. package/dist/checks/workflows/deploy.d.ts.map +1 -0
  651. package/dist/checks/workflows/deploy.js +107 -0
  652. package/dist/checks/workflows/deploy.js.map +1 -0
  653. package/dist/checks/workflows/deployment-readiness.d.ts +12 -0
  654. package/dist/checks/workflows/deployment-readiness.d.ts.map +1 -0
  655. package/dist/checks/workflows/deployment-readiness.js +403 -0
  656. package/dist/checks/workflows/deployment-readiness.js.map +1 -0
  657. package/dist/checks/workflows/dev.d.ts +19 -0
  658. package/dist/checks/workflows/dev.d.ts.map +1 -0
  659. package/dist/checks/workflows/dev.js +88 -0
  660. package/dist/checks/workflows/dev.js.map +1 -0
  661. package/dist/checks/workflows/development.d.ts +9 -0
  662. package/dist/checks/workflows/development.d.ts.map +1 -0
  663. package/dist/checks/workflows/development.js +65 -0
  664. package/dist/checks/workflows/development.js.map +1 -0
  665. package/dist/checks/workflows/enterprise.d.ts +10 -0
  666. package/dist/checks/workflows/enterprise.d.ts.map +1 -0
  667. package/dist/checks/workflows/enterprise.js +359 -0
  668. package/dist/checks/workflows/enterprise.js.map +1 -0
  669. package/dist/checks/workflows/images.d.ts +6 -0
  670. package/dist/checks/workflows/images.d.ts.map +1 -0
  671. package/dist/checks/workflows/images.js +58 -0
  672. package/dist/checks/workflows/images.js.map +1 -0
  673. package/dist/checks/workflows/naming.d.ts +19 -0
  674. package/dist/checks/workflows/naming.d.ts.map +1 -0
  675. package/dist/checks/workflows/naming.js +42 -0
  676. package/dist/checks/workflows/naming.js.map +1 -0
  677. package/dist/checks/workflows/performance.d.ts +8 -0
  678. package/dist/checks/workflows/performance.d.ts.map +1 -0
  679. package/dist/checks/workflows/performance.js +77 -0
  680. package/dist/checks/workflows/performance.js.map +1 -0
  681. package/dist/checks/workflows/pre-deploy.d.ts +6 -0
  682. package/dist/checks/workflows/pre-deploy.d.ts.map +1 -0
  683. package/dist/checks/workflows/pre-deploy.js +41 -0
  684. package/dist/checks/workflows/pre-deploy.js.map +1 -0
  685. package/dist/checks/workflows/security.d.ts +8 -0
  686. package/dist/checks/workflows/security.d.ts.map +1 -0
  687. package/dist/checks/workflows/security.js +71 -0
  688. package/dist/checks/workflows/security.js.map +1 -0
  689. package/dist/checks/workflows/supercatch.d.ts +8 -0
  690. package/dist/checks/workflows/supercatch.d.ts.map +1 -0
  691. package/dist/checks/workflows/supercatch.js +127 -0
  692. package/dist/checks/workflows/supercatch.js.map +1 -0
  693. package/dist/checks/workflows/ui-quality.d.ts +9 -0
  694. package/dist/checks/workflows/ui-quality.d.ts.map +1 -0
  695. package/dist/checks/workflows/ui-quality.js +264 -0
  696. package/dist/checks/workflows/ui-quality.js.map +1 -0
  697. package/dist/checks/workflows/ui-uniformity.d.ts +18 -0
  698. package/dist/checks/workflows/ui-uniformity.d.ts.map +1 -0
  699. package/dist/checks/workflows/ui-uniformity.js +265 -0
  700. package/dist/checks/workflows/ui-uniformity.js.map +1 -0
  701. package/dist/checks/workflows/vercel.d.ts +16 -0
  702. package/dist/checks/workflows/vercel.d.ts.map +1 -0
  703. package/dist/checks/workflows/vercel.js +173 -0
  704. package/dist/checks/workflows/vercel.js.map +1 -0
  705. package/dist/utils/validation-helpers.d.ts +43 -0
  706. package/dist/utils/validation-helpers.d.ts.map +1 -0
  707. package/dist/utils/validation-helpers.js +370 -0
  708. package/dist/utils/validation-helpers.js.map +1 -0
  709. package/package.json +1 -1
@@ -0,0 +1,2008 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ /**
4
+ * Consolidated UI Components Preflight
5
+ *
6
+ * Combines component checks into one comprehensive module:
7
+ * - button-variant-consistency (button patterns, MUI → new variants)
8
+ * - badge-overflow-validation (badge positioning)
9
+ * - icon-size-consistency (icon sizing)
10
+ * - border-radius-consistency (border radius tokens)
11
+ * - shadow-consistency (shadow tokens)
12
+ * - chip-consistency (chip height & patterns)
13
+ * - platform-icons (duplicate platform icon definitions)
14
+ * - numeric-spacing (hardcoded spacing values)
15
+ * - hardcoded-colors (internal colors should use tokens)
16
+ * - card-outline-consistency (cards should use shadows, not borders)
17
+ * - metric-grid-glass-variant (MetricGrid glass variant has unreliable borders) - BLOCKING
18
+ * - chained-css-variables (CSS variables that chain may not resolve) - BLOCKING
19
+ * - duplicate-classname-props (multiple className props overwrite each other) - BLOCKING
20
+ * - deprecated-system-components (ButtonSystem, FlexSystem, CardSystem, GridSystem, FeedbackSystem)
21
+ * - custom-stat-displays (Avatar + Typography patterns should use StatCardGrid)
22
+ *
23
+ * All issues are warnings (non-blocking) to allow gradual migration.
24
+ *
25
+ * Usage:
26
+ * pnpm preflight:ui-components # All component checks
27
+ * pnpm preflight:ui-components buttons # Button patterns only
28
+ * pnpm preflight:ui-components badges # Badge overflow only
29
+ * pnpm preflight:ui-components icons # Icon sizes only
30
+ * pnpm preflight:ui-components radius # Border radius only
31
+ * pnpm preflight:ui-components shadows # Shadows only
32
+ * pnpm preflight:ui-components chips # Chip consistency only
33
+ * pnpm preflight:ui-components platforms # Platform icons only
34
+ * pnpm preflight:ui-components spacing # Numeric spacing only
35
+ * pnpm preflight:ui-components colors # Hardcoded colors only
36
+ * pnpm preflight:ui-components cards # Card outline consistency only
37
+ * pnpm preflight:ui-components metric-grid # MetricGrid glass variant check
38
+ * pnpm preflight:ui-components css-variables # Chained CSS variable check
39
+ * pnpm preflight:ui-components classname # Duplicate className props check
40
+ * pnpm preflight:ui-components deprecated # Deprecated System components only
41
+ * pnpm preflight:ui-components stat-displays # Custom stat display detection
42
+ */
43
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
44
+ if (k2 === undefined) k2 = k;
45
+ var desc = Object.getOwnPropertyDescriptor(m, k);
46
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
47
+ desc = { enumerable: true, get: function() { return m[k]; } };
48
+ }
49
+ Object.defineProperty(o, k2, desc);
50
+ }) : (function(o, m, k, k2) {
51
+ if (k2 === undefined) k2 = k;
52
+ o[k2] = m[k];
53
+ }));
54
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
55
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
56
+ }) : function(o, v) {
57
+ o["default"] = v;
58
+ });
59
+ var __importStar = (this && this.__importStar) || (function () {
60
+ var ownKeys = function(o) {
61
+ ownKeys = Object.getOwnPropertyNames || function (o) {
62
+ var ar = [];
63
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
64
+ return ar;
65
+ };
66
+ return ownKeys(o);
67
+ };
68
+ return function (mod) {
69
+ if (mod && mod.__esModule) return mod;
70
+ var result = {};
71
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
72
+ __setModuleDefault(result, mod);
73
+ return result;
74
+ };
75
+ })();
76
+ Object.defineProperty(exports, "__esModule", { value: true });
77
+ exports.UIComponentsPreflightModule = void 0;
78
+ const fs = __importStar(require("fs"));
79
+ const path = __importStar(require("path"));
80
+ const console_chars_1 = require("../../utils/console-chars");
81
+ const file_cache_1 = require("../../shared/file-cache");
82
+ const glob_patterns_1 = require("../../shared/glob-patterns");
83
+ const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
84
+ const exclusions_1 = require("../../shared/exclusions");
85
+ const concurrency_config_1 = require("../../shared/concurrency-config");
86
+ // Check metadata
87
+ const id = "consolidated/ui-components";
88
+ // Essential defaults (node_modules, __mocks__, .next, dist, coverage)
89
+ const EXCLUDED = (0, glob_patterns_1.extendExcludes)(glob_patterns_1.STANDARD_EXCLUDES_WITH_TESTS, ["**/packages/ui/**"]);
90
+ // App-level exclusions loaded from config
91
+ let appExclusions = [];
92
+ // Platform brand colors (OK to hardcode - external brands)
93
+ const PLATFORM_BRAND_COLORS = [
94
+ "#E53238", // eBay
95
+ "#232F3E",
96
+ "#FF9900", // Amazon
97
+ "#FFC220",
98
+ "#0071CE", // Walmart
99
+ "#F16521", // Etsy
100
+ "#1A1A1A",
101
+ "#FF6B35", // Whatnot
102
+ "#00F2EA",
103
+ "#FE2C55", // TikTok
104
+ "#7AB55C", // Shopify
105
+ "#96588A", // WooCommerce
106
+ "#6772E5", // Shippo
107
+ "#3B5998", // ShipStation
108
+ ].map((c) => c.toLowerCase());
109
+ // CACHED FILE LISTS - Scan once, use everywhere
110
+ let _cachedAppComponentsTsxFiles = null;
111
+ async function getAppComponentsTsxFiles() {
112
+ if (!_cachedAppComponentsTsxFiles) {
113
+ _cachedAppComponentsTsxFiles = await file_cache_1.fileCache.getAppAndComponentsTSX();
114
+ }
115
+ return _cachedAppComponentsTsxFiles;
116
+ }
117
+ // Get concurrency from shared config (respects PREFLIGHT_CONCURRENCY env var)
118
+ const concurrencyConfig = (0, concurrency_config_1.getConcurrencyConfig)();
119
+ class UIComponentsPreflightModule {
120
+ verbose;
121
+ parallel = false;
122
+ strictUniformity;
123
+ constructor(options = {}) {
124
+ this.verbose = options.verbose || false;
125
+ this.parallel = options.parallel || concurrencyConfig.parallel;
126
+ this.strictUniformity = process.env.PREFLIGHT_STRICT_UNIFORMITY === "1";
127
+ }
128
+ getLineNumberFromIndex(content, index) {
129
+ return content.slice(0, index).split("\n").length;
130
+ }
131
+ isLikelyInteractive(tag) {
132
+ return (/\bonClick=/.test(tag) ||
133
+ /\bonKeyDown=/.test(tag) ||
134
+ /\bonKeyUp=/.test(tag) ||
135
+ /\brole=\{?['"]button['"]\}?/.test(tag) ||
136
+ /\btabIndex=\{?0\}?/.test(tag));
137
+ }
138
+ async getComponentFiles() {
139
+ const files = await file_cache_1.fileCache.getComponentsTsxJsxFiles();
140
+ return files.filter((file) => !(0, exclusions_1.shouldExcludeFile)(file, appExclusions));
141
+ }
142
+ async getAllFiles() {
143
+ const files = await file_cache_1.fileCache.getAppAndComponentsTSX();
144
+ return files.filter((file) => !(0, exclusions_1.shouldExcludeFile)(file, appExclusions));
145
+ }
146
+ /**
147
+ * UnifiedDialog padding guardrail
148
+ * Ensures the shared UnifiedDialog enforces internal padding so dialog content
149
+ * can't regress to flush-to-border when underlying dialog primitives change.
150
+ */
151
+ async checkUnifiedDialogPadding() {
152
+ const startTime = Date.now();
153
+ const issues = [];
154
+ const file = "components/shared/unified-dialog/UnifiedDialog.tsx";
155
+ try {
156
+ const content = fs.readFileSync(file, "utf8");
157
+ const titleHasPadding = /<DialogTitle[^>]*\bclassName\s*=\s*"[^"]*\bp-\d+[^"]*"/m.test(content);
158
+ const contentHasPadding = /<DialogContent[^>]*\bclassName\s*=\s*"[^"]*\bp-\d+[^"]*"/m.test(content);
159
+ if (!titleHasPadding || !contentHasPadding) {
160
+ issues.push({
161
+ file,
162
+ line: 1,
163
+ type: "unified-dialog-padding",
164
+ severity: "warning",
165
+ message: "UnifiedDialog should enforce internal padding on DialogTitle and DialogContent to prevent flush-to-border dialogs.",
166
+ suggestion: "Add a padding className (e.g., p-6) to <DialogTitle> and <DialogContent> in UnifiedDialog.",
167
+ });
168
+ }
169
+ }
170
+ catch {
171
+ issues.push({
172
+ file,
173
+ line: 1,
174
+ type: "unified-dialog-padding",
175
+ severity: "warning",
176
+ message: "Could not read UnifiedDialog implementation to validate padding guardrail.",
177
+ });
178
+ }
179
+ return {
180
+ name: "UnifiedDialog padding guardrail",
181
+ passed: issues.length === 0,
182
+ blocking: true,
183
+ issues,
184
+ duration: Date.now() - startTime,
185
+ };
186
+ }
187
+ /**
188
+ * Button Variant Consistency
189
+ * Detects MUI-style variants that should use new button system
190
+ */
191
+ async checkButtons() {
192
+ const startTime = Date.now();
193
+ const issues = [];
194
+ const files = await this.getAllFiles();
195
+ for (const file of files) {
196
+ const content = fs.readFileSync(file, "utf8");
197
+ const lines = content.split("\n");
198
+ // Normalize path separators for cross-platform compatibility
199
+ const normalizedFile = file.replace(/\\/g, "/");
200
+ const isUploadSurface = normalizedFile.includes("components/upload/") ||
201
+ normalizedFile.includes("components/shared/file/");
202
+ // components/ui contains the implementation of the design system primitives
203
+ // (it is expected to use native elements internally).
204
+ const isUIPrimitive = normalizedFile.includes("components/ui/");
205
+ lines.forEach((line, index) => {
206
+ if (line.trim().startsWith("//"))
207
+ return;
208
+ // MUI variant="contained" → variant="default"
209
+ if (/variant=["']contained["']/i.test(line)) {
210
+ issues.push({
211
+ file,
212
+ line: index + 1,
213
+ type: "mui-contained-variant",
214
+ severity: "warning",
215
+ message: 'MUI variant="contained" should be variant="default"',
216
+ suggestion: 'Change to variant="default" for consistency',
217
+ snippet: line.trim().substring(0, 80),
218
+ });
219
+ }
220
+ // MUI variant="outlined" → variant="outline"
221
+ if (/variant=["']outlined["']/i.test(line) && /<Button/i.test(line)) {
222
+ issues.push({
223
+ file,
224
+ line: index + 1,
225
+ type: "mui-outlined-variant",
226
+ severity: "warning",
227
+ message: 'MUI variant="outlined" should be variant="outline"',
228
+ suggestion: 'Change to variant="outline" for consistency',
229
+ snippet: line.trim().substring(0, 80),
230
+ });
231
+ }
232
+ // Native button usage (upload surfaces should use shared UI components)
233
+ if (isUploadSurface && /<button\b/.test(line)) {
234
+ issues.push({
235
+ file,
236
+ line: index + 1,
237
+ type: "native-button-upload-surface",
238
+ severity: "warning",
239
+ message: "Native <button> used in upload UI",
240
+ suggestion: "Use <Button> or <IconButton> from @/components/ui for consistent spacing/typography",
241
+ snippet: line.trim().substring(0, 80),
242
+ });
243
+ }
244
+ // Native button usage (global): encourage consistent use of design-system buttons everywhere.
245
+ // Keep this warning out of the primitives folder to avoid noise.
246
+ if (!isUIPrimitive && !isUploadSurface && /<button\b/.test(line)) {
247
+ issues.push({
248
+ file,
249
+ line: index + 1,
250
+ type: "native-button-global",
251
+ severity: "warning",
252
+ message: "Native <button> used outside the UI system",
253
+ suggestion: "Prefer <Button> / <IconButton> from @/components/ui to keep spacing and typography uniform",
254
+ snippet: line.trim().substring(0, 80),
255
+ });
256
+ }
257
+ // Native button with inline styles
258
+ if (/<button[^>]*style=/.test(line)) {
259
+ issues.push({
260
+ file,
261
+ line: index + 1,
262
+ type: "native-button-styles",
263
+ severity: "warning",
264
+ message: "Native <button> with inline styles",
265
+ suggestion: "Use <Button> from @/components/ui",
266
+ snippet: line.trim().substring(0, 80),
267
+ });
268
+ }
269
+ // Button with bg className
270
+ if (/<Button[^>]*className=["'][^"']*bg-/i.test(line)) {
271
+ issues.push({
272
+ file,
273
+ line: index + 1,
274
+ type: "button-bg-class",
275
+ severity: "warning",
276
+ message: "Button using className for background",
277
+ suggestion: "Use variant prop instead",
278
+ snippet: line.trim().substring(0, 80),
279
+ });
280
+ }
281
+ // Check for Button asChild with Link that might have centering issues
282
+ // The Button component should handle this, but warn if custom styles override
283
+ if (/<Button[^>]*asChild/.test(line)) {
284
+ const context = lines.slice(index, Math.min(lines.length, index + 5)).join("\n");
285
+ // Check if Link has styles that might break centering
286
+ if (context.includes("style=") && context.includes("<Link")) {
287
+ // Look for height or display overrides that could affect centering
288
+ if (/style=\{[^}]*(height|display|alignItems|justifyContent)/.test(context)) {
289
+ issues.push({
290
+ file,
291
+ line: index + 1,
292
+ type: "button-asChild-style-override",
293
+ severity: "info",
294
+ message: "Button asChild with style overrides on Link",
295
+ suggestion: "Ensure Link styles don't break text centering (height, display, alignItems, justifyContent)",
296
+ snippet: line.trim().substring(0, 80),
297
+ });
298
+ }
299
+ }
300
+ }
301
+ });
302
+ }
303
+ return {
304
+ name: "Button Variant Consistency",
305
+ passed: true,
306
+ blocking: true,
307
+ issues,
308
+ duration: Date.now() - startTime,
309
+ };
310
+ }
311
+ /**
312
+ * Link Styled As Button
313
+ * Detects <Link>/<a> elements that are visually styled like buttons.
314
+ * Prefer <Button asChild> for consistent spacing/typography.
315
+ */
316
+ async checkLinkButtons() {
317
+ const startTime = Date.now();
318
+ const issues = [];
319
+ const files = await this.getAllFiles();
320
+ for (const file of files) {
321
+ // UI primitives may legitimately render anchors/buttons internally
322
+ const normalizedFile = file.replace(/\\/g, "/");
323
+ if (normalizedFile.includes("components/ui/"))
324
+ continue;
325
+ const content = fs.readFileSync(file, "utf8");
326
+ // Match opening tags for Link or a (can span multiple lines)
327
+ const tagMatches = content.matchAll(/<(Link|a)\b[\s\S]*?>/g);
328
+ for (const match of tagMatches) {
329
+ const fullTag = match[0];
330
+ const startIndex = match.index ?? 0;
331
+ // Ignore if already wrapped via Button asChild in the same opening tag context
332
+ // (Not perfect, but avoids common false positives)
333
+ if (fullTag.includes("asChild") && fullTag.includes("<Button"))
334
+ continue;
335
+ const hasClassName = /className=\{?["']/.test(fullTag);
336
+ const hasStyle = /\bstyle=\{/.test(fullTag);
337
+ if (!hasClassName && !hasStyle)
338
+ continue;
339
+ // Skip accessibility skip-links (sr-only pattern)
340
+ if (/sr-only/.test(fullTag))
341
+ continue;
342
+ // Skip navigation card/tile patterns (flex-col with items-center = card layout, not button)
343
+ // These are semantically links for navigation, not action buttons
344
+ if (/flex-col/.test(fullTag) && /items-center/.test(fullTag))
345
+ continue;
346
+ // Skip grid-based category/navigation tiles
347
+ if (/grid/.test(fullTag) || /text-center/.test(fullTag))
348
+ continue;
349
+ // Heuristic: if it has padding + button-like affordances, it's likely a button.
350
+ // (We keep this intentionally conservative to avoid noise.)
351
+ const looksLikeButton = /\b(px|py|p)-\d+\b/.test(fullTag) &&
352
+ (/(bg-|border|rounded|shadow|hover:|focus:|inline-flex|items-center|justify-center)/.test(fullTag) ||
353
+ /\bvariant=\{?['"](?:default|outline|ghost|text)['"]\}?/.test(fullTag));
354
+ if (!looksLikeButton)
355
+ continue;
356
+ issues.push({
357
+ file,
358
+ line: this.getLineNumberFromIndex(content, startIndex),
359
+ type: "link-styled-as-button",
360
+ severity: "warning",
361
+ message: "<Link>/<a> styled like a button",
362
+ suggestion: "Wrap the Link with <Button asChild> from @/components/ui for uniform button spacing/typography",
363
+ snippet: fullTag.replace(/\s+/g, " ").trim().substring(0, 120),
364
+ });
365
+ }
366
+ }
367
+ return {
368
+ name: "Link Styled As Button",
369
+ passed: true,
370
+ blocking: true,
371
+ issues,
372
+ duration: Date.now() - startTime,
373
+ };
374
+ }
375
+ /**
376
+ * Clickable Non-Button Elements
377
+ * Detects <div>/<span> used as interactive controls.
378
+ * Prefer <Button>/<IconButton> for consistent spacing/typography and a11y.
379
+ */
380
+ async checkClickableNonButtons() {
381
+ const startTime = Date.now();
382
+ const issues = [];
383
+ const files = await this.getAllFiles();
384
+ for (const file of files) {
385
+ // Normalize path separators for cross-platform compatibility
386
+ const normalizedFile = file.replace(/\\/g, "/");
387
+ if (normalizedFile.includes("components/ui/"))
388
+ continue;
389
+ const content = fs.readFileSync(file, "utf8");
390
+ // Match opening tags for div/span (can span multiple lines)
391
+ const tagMatches = content.matchAll(/<(div|span)\b[\s\S]*?>/g);
392
+ for (const match of tagMatches) {
393
+ const tag = match[0];
394
+ const startIndex = match.index ?? 0;
395
+ if (!this.isLikelyInteractive(tag))
396
+ continue;
397
+ // Skip clearly non-control patterns
398
+ if (/\brole=\{?['"]presentation['"]\}?/.test(tag))
399
+ continue;
400
+ if (/\baria-hidden=\{?['"]true['"]\}?/.test(tag))
401
+ continue;
402
+ // Heuristic: treat as "button-like" if it advertises interactivity
403
+ const hasPointerCue = /cursor-pointer/.test(tag);
404
+ const hasButtonRole = /\brole=\{?['"]button['"]\}?/.test(tag);
405
+ if (!hasPointerCue && !hasButtonRole)
406
+ continue;
407
+ issues.push({
408
+ file,
409
+ line: this.getLineNumberFromIndex(content, startIndex),
410
+ type: "clickable-non-button",
411
+ severity: "warning",
412
+ message: "Clickable <div>/<span> used as a control",
413
+ suggestion: "Use <Button> / <IconButton> from @/components/ui (or <Button asChild>) for uniform behavior and accessibility",
414
+ snippet: tag.replace(/\s+/g, " ").trim().substring(0, 120),
415
+ });
416
+ }
417
+ }
418
+ return {
419
+ name: "Clickable Non-Buttons",
420
+ passed: true,
421
+ blocking: true,
422
+ issues,
423
+ duration: Date.now() - startTime,
424
+ };
425
+ }
426
+ /**
427
+ * IconButton Accessibility
428
+ * Ensures IconButton has an accessible label (label / aria-label / title).
429
+ */
430
+ async checkIconButtonAccessibility() {
431
+ const startTime = Date.now();
432
+ const issues = [];
433
+ const files = await this.getAllFiles();
434
+ for (const file of files) {
435
+ // UI primitives define IconButton itself
436
+ const normalizedFile = file.replace(/\\/g, "/");
437
+ if (normalizedFile.includes("components/ui/"))
438
+ continue;
439
+ const content = fs.readFileSync(file, "utf8");
440
+ // Find all IconButton opening tags - need to handle JSX expressions inside props
441
+ // which may contain > characters (e.g., icon={<Trash2 size={16} />})
442
+ const iconButtonStarts = [...content.matchAll(/<IconButton\b/g)];
443
+ for (const startMatch of iconButtonStarts) {
444
+ const startIndex = startMatch.index ?? 0;
445
+ // Find the closing > of this tag, accounting for nested JSX in props
446
+ let depth = 0;
447
+ let inString = false;
448
+ let stringChar = "";
449
+ let tagEnd = startIndex;
450
+ for (let i = startIndex; i < content.length; i++) {
451
+ const char = content[i];
452
+ const prevChar = i > 0 ? content[i - 1] : "";
453
+ // Track string boundaries
454
+ if ((char === '"' || char === "'" || char === "`") && prevChar !== "\\") {
455
+ if (!inString) {
456
+ inString = true;
457
+ stringChar = char;
458
+ }
459
+ else if (char === stringChar) {
460
+ inString = false;
461
+ }
462
+ continue;
463
+ }
464
+ if (inString)
465
+ continue;
466
+ // Track JSX expression depth
467
+ if (char === "{") {
468
+ depth++;
469
+ }
470
+ else if (char === "}") {
471
+ depth--;
472
+ }
473
+ else if (char === ">" && depth === 0) {
474
+ tagEnd = i + 1;
475
+ break;
476
+ }
477
+ }
478
+ const tag = content.slice(startIndex, tagEnd);
479
+ // Check for accessibility props
480
+ const hasLabel = /\blabel=/.test(tag);
481
+ const hasAria = /\baria-label=/.test(tag);
482
+ const hasTitle = /\btitle=/.test(tag);
483
+ if (hasLabel || hasAria || hasTitle)
484
+ continue;
485
+ issues.push({
486
+ file,
487
+ line: this.getLineNumberFromIndex(content, startIndex),
488
+ type: "iconbutton-missing-label",
489
+ severity: "warning",
490
+ message: "IconButton missing accessibility label",
491
+ suggestion: 'Add label="..." (preferred) or aria-label="..."',
492
+ snippet: tag.replace(/\s+/g, " ").trim().substring(0, 120),
493
+ });
494
+ }
495
+ }
496
+ return {
497
+ name: "IconButton Accessibility",
498
+ passed: true,
499
+ blocking: true,
500
+ issues,
501
+ duration: Date.now() - startTime,
502
+ };
503
+ }
504
+ /**
505
+ * Badge Overflow Validation
506
+ */
507
+ async checkBadges() {
508
+ const startTime = Date.now();
509
+ const issues = [];
510
+ const files = await getAppComponentsTsxFiles();
511
+ for (const file of files) {
512
+ const content = fs.readFileSync(file, "utf8");
513
+ const lines = content.split("\n");
514
+ lines.forEach((line, index) => {
515
+ // Badge with negative top position
516
+ if (/<(Chip|Badge|div)[^>]*position:\s*["']absolute["'][^>]*top:\s*-\d+/i.test(line)) {
517
+ const context = lines
518
+ .slice(Math.max(0, index - 10), Math.min(lines.length, index + 10))
519
+ .join("\n");
520
+ if (!context.includes('overflow: "visible"') &&
521
+ !context.includes("overflow: 'visible'")) {
522
+ issues.push({
523
+ file,
524
+ line: index + 1,
525
+ type: "badge-overflow",
526
+ severity: "warning",
527
+ message: "Badge with negative top without overflow: visible",
528
+ suggestion: 'Add overflow: "visible" to parent',
529
+ snippet: line.trim().substring(0, 80),
530
+ });
531
+ }
532
+ }
533
+ });
534
+ }
535
+ return {
536
+ name: "Badge Overflow Validation",
537
+ passed: true,
538
+ blocking: true,
539
+ issues,
540
+ duration: Date.now() - startTime,
541
+ };
542
+ }
543
+ /**
544
+ * Icon Size Consistency
545
+ */
546
+ async checkIcons() {
547
+ const startTime = Date.now();
548
+ const issues = [];
549
+ const files = await this.getAllFiles();
550
+ for (const file of files) {
551
+ const content = fs.readFileSync(file, "utf8");
552
+ const lines = content.split("\n");
553
+ lines.forEach((line, index) => {
554
+ if (line.trim().startsWith("//"))
555
+ return;
556
+ // Hardcoded icon size (non-standard)
557
+ const sizeMatch = line.match(/size=\{?(\d+)\}?/);
558
+ if (sizeMatch && (line.includes("lucide-react") || line.includes("Icon"))) {
559
+ const size = parseInt(sizeMatch[1]);
560
+ if (![16, 20, 24, 32].includes(size)) {
561
+ issues.push({
562
+ file,
563
+ line: index + 1,
564
+ type: "hardcoded-icon-size",
565
+ severity: "info",
566
+ message: `Non-standard icon size: ${size}`,
567
+ suggestion: "Use 16, 20, 24, or 32",
568
+ snippet: line.trim().substring(0, 80),
569
+ });
570
+ }
571
+ }
572
+ });
573
+ }
574
+ return {
575
+ name: "Icon Size Consistency",
576
+ passed: true,
577
+ blocking: true,
578
+ issues,
579
+ duration: Date.now() - startTime,
580
+ };
581
+ }
582
+ /**
583
+ * Border Radius Consistency
584
+ */
585
+ async checkBorderRadius() {
586
+ const startTime = Date.now();
587
+ const issues = [];
588
+ const files = await this.getAllFiles();
589
+ for (const file of files) {
590
+ const content = fs.readFileSync(file, "utf8");
591
+ const lines = content.split("\n");
592
+ lines.forEach((line, index) => {
593
+ if (line.trim().startsWith("//"))
594
+ return;
595
+ // Hardcoded border-radius (numeric)
596
+ if (/borderRadius:\s*(\d+)\s*[,}]/i.test(line) && !line.includes("var(--radius-")) {
597
+ issues.push({
598
+ file,
599
+ line: index + 1,
600
+ type: "hardcoded-radius-numeric",
601
+ severity: "warning",
602
+ message: "Hardcoded numeric border-radius",
603
+ suggestion: "Use var(--radius-sm|md|lg|xl)",
604
+ snippet: line.trim().substring(0, 80),
605
+ });
606
+ }
607
+ // Hardcoded border-radius with px/rem
608
+ if (/borderRadius:\s*['"]?(\d+px|\d+rem)['"]?/i.test(line) &&
609
+ !line.includes("var(--radius-")) {
610
+ issues.push({
611
+ file,
612
+ line: index + 1,
613
+ type: "hardcoded-radius",
614
+ severity: "warning",
615
+ message: "Hardcoded border-radius",
616
+ suggestion: "Use var(--radius-sm|md|lg|xl)",
617
+ snippet: line.trim().substring(0, 80),
618
+ });
619
+ }
620
+ // Tailwind rounded classes
621
+ const rounded = line.match(/\brounded-(sm|md|lg|xl|2xl|full)\b/);
622
+ if (rounded) {
623
+ issues.push({
624
+ file,
625
+ line: index + 1,
626
+ type: "tailwind-rounded",
627
+ severity: "info",
628
+ message: `Tailwind ${rounded[0]} class`,
629
+ suggestion: `Use style={{ borderRadius: "var(--radius-${rounded[1]})" }}`,
630
+ snippet: line.trim().substring(0, 80),
631
+ });
632
+ }
633
+ });
634
+ }
635
+ return {
636
+ name: "Border Radius Consistency",
637
+ passed: true,
638
+ blocking: true,
639
+ issues,
640
+ duration: Date.now() - startTime,
641
+ };
642
+ }
643
+ /**
644
+ * Shadow Consistency
645
+ */
646
+ async checkShadows() {
647
+ const startTime = Date.now();
648
+ const issues = [];
649
+ const files = await this.getAllFiles();
650
+ for (const file of files) {
651
+ const content = fs.readFileSync(file, "utf8");
652
+ const lines = content.split("\n");
653
+ lines.forEach((line, index) => {
654
+ if (line.trim().startsWith("//"))
655
+ return;
656
+ // Hardcoded box-shadow
657
+ const shadowMatch = line.match(/boxShadow:\s*['"]([^'"]+)['"]?/i);
658
+ if (shadowMatch &&
659
+ !line.includes("var(--shadow-") &&
660
+ !line.includes("var(--glass-shadow)")) {
661
+ const value = shadowMatch[1];
662
+ if (value !== "none" && value !== "inherit") {
663
+ issues.push({
664
+ file,
665
+ line: index + 1,
666
+ type: "hardcoded-shadow",
667
+ severity: "warning",
668
+ message: "Hardcoded box-shadow",
669
+ suggestion: "Use var(--shadow-sm|md|lg|xl)",
670
+ snippet: line.trim().substring(0, 80),
671
+ });
672
+ }
673
+ }
674
+ // Tailwind shadow classes - but exclude hover: patterns (those are valid CSS interactions)
675
+ // Also exclude if already using shadow-[var(--shadow-xl)] syntax (correct pattern)
676
+ // Also exclude if inside var(--shadow-*) token usage
677
+ const tailwindShadow = line.match(/(?<!hover:)(?<!focus:)(?<!active:)(?<!var\(--)\bshadow-(sm|md|lg|xl|2xl)\b/);
678
+ if (tailwindShadow &&
679
+ !line.includes("shadow-[var(--shadow-") &&
680
+ !line.includes("var(--shadow-")) {
681
+ issues.push({
682
+ file,
683
+ line: index + 1,
684
+ type: "tailwind-shadow",
685
+ severity: "warning",
686
+ message: `Tailwind ${tailwindShadow[0]} class`,
687
+ suggestion: `Use style={{ boxShadow: "var(--shadow-${tailwindShadow[1]})" }}`,
688
+ snippet: line.trim().substring(0, 80),
689
+ });
690
+ }
691
+ });
692
+ }
693
+ return {
694
+ name: "Shadow Consistency",
695
+ passed: true,
696
+ blocking: true,
697
+ issues,
698
+ duration: Date.now() - startTime,
699
+ };
700
+ }
701
+ /**
702
+ * Chip Height Consistency
703
+ * Ensures all chips use consistent heights via StatusChip or proper size props
704
+ */
705
+ async checkChips() {
706
+ const startTime = Date.now();
707
+ const issues = [];
708
+ const allFiles = await getAppComponentsTsxFiles();
709
+ for (const file of allFiles) {
710
+ const content = fs.readFileSync(file, "utf8");
711
+ const lines = content.split("\n");
712
+ // Check if file uses StatusChip (preferred for status indicators)
713
+ const usesStatusChip = content.includes("StatusChip") || content.includes("CategoryChip");
714
+ lines.forEach((line, index) => {
715
+ if (line.trim().startsWith("//"))
716
+ return;
717
+ // Check for Chip with custom height overrides
718
+ if (/<Chip[^>]*sx=\{[^}]*height:/i.test(line)) {
719
+ issues.push({
720
+ file,
721
+ line: index + 1,
722
+ type: "chip-custom-height",
723
+ severity: "info",
724
+ message: "Chip with custom height override",
725
+ suggestion: 'Use size="sm" (20px) or size="md" (24px), or use StatusChip for status indicators',
726
+ snippet: line.trim().substring(0, 80),
727
+ });
728
+ }
729
+ // Check for status-like Chip that should use StatusChip
730
+ if (/<Chip[^>]*label=["'](Active|Inactive|Pending|Available|Coming Soon|Connected|Beta|Draft|Published)/i.test(line) &&
731
+ !usesStatusChip) {
732
+ issues.push({
733
+ file,
734
+ line: index + 1,
735
+ type: "chip-status-pattern",
736
+ severity: "info",
737
+ message: "Status indicator using Chip instead of StatusChip",
738
+ suggestion: 'Use <StatusChip status="active" /> for consistent status indicators',
739
+ snippet: line.trim().substring(0, 80),
740
+ });
741
+ }
742
+ // Check for Chip without explicit size prop (inconsistent sizing)
743
+ if (/<Chip[^>]*label=/i.test(line) &&
744
+ !/<Chip[^>]*size=/i.test(line) &&
745
+ !line.includes("StatusChip")) {
746
+ issues.push({
747
+ file,
748
+ line: index + 1,
749
+ type: "chip-no-size",
750
+ severity: "info",
751
+ message: "Chip without explicit size prop",
752
+ suggestion: 'Add size="sm" or size="md" for consistent sizing',
753
+ snippet: line.trim().substring(0, 80),
754
+ });
755
+ }
756
+ });
757
+ }
758
+ return {
759
+ name: "Chip Consistency",
760
+ passed: true,
761
+ blocking: true,
762
+ issues,
763
+ duration: Date.now() - startTime,
764
+ };
765
+ }
766
+ /**
767
+ * Platform Icons Consistency
768
+ * Detects duplicate platform icon definitions that should use PlatformIcon component
769
+ */
770
+ async checkPlatformIcons() {
771
+ const startTime = Date.now();
772
+ const issues = [];
773
+ const allFiles = await getAppComponentsTsxFiles();
774
+ // Platform brand colors that indicate inline platform icons
775
+ const platformPatterns = [
776
+ { pattern: /#E53238/i, platform: "eBay" },
777
+ { pattern: /#232F3E.*#FF9900|#FF9900.*#232F3E/i, platform: "Amazon" },
778
+ { pattern: /#FFC220.*#0071CE|#0071CE.*#FFC220/i, platform: "Walmart" },
779
+ { pattern: /#F16521/i, platform: "Etsy" },
780
+ { pattern: /#1A1A1A.*#FF6B35|#FF6B35.*#1A1A1A/i, platform: "Whatnot" },
781
+ { pattern: /#00F2EA|#FE2C55/i, platform: "TikTok" },
782
+ { pattern: /#7AB55C/i, platform: "Shopify" },
783
+ { pattern: /#96588A/i, platform: "WooCommerce" },
784
+ ];
785
+ for (const file of allFiles) {
786
+ const content = fs.readFileSync(file, "utf8");
787
+ // Skip if file imports PlatformIcon
788
+ if (content.includes("PlatformIcon"))
789
+ continue;
790
+ const lines = content.split("\n");
791
+ lines.forEach((line, index) => {
792
+ if (line.trim().startsWith("//"))
793
+ return;
794
+ // Check for inline platform icon patterns
795
+ for (const { pattern, platform } of platformPatterns) {
796
+ if (pattern.test(line) &&
797
+ (line.includes("Avatar") || line.includes("bgcolor") || line.includes("Box"))) {
798
+ issues.push({
799
+ file,
800
+ line: index + 1,
801
+ type: "inline-platform-icon",
802
+ severity: "info",
803
+ message: `Inline ${platform} icon definition`,
804
+ suggestion: `Use <PlatformIcon platform="${platform.toLowerCase()}" /> for consistency`,
805
+ snippet: line.trim().substring(0, 80),
806
+ });
807
+ break; // Only report once per line
808
+ }
809
+ }
810
+ });
811
+ }
812
+ return {
813
+ name: "Platform Icons",
814
+ passed: true,
815
+ blocking: true,
816
+ issues,
817
+ duration: Date.now() - startTime,
818
+ };
819
+ }
820
+ /**
821
+ * Numeric Spacing Consistency
822
+ * Detects hardcoded numeric spacing values that should use design tokens
823
+ */
824
+ async checkNumericSpacing() {
825
+ const startTime = Date.now();
826
+ const issues = [];
827
+ const allFiles = await getAppComponentsTsxFiles();
828
+ for (const file of allFiles) {
829
+ const content = fs.readFileSync(file, "utf8");
830
+ const lines = content.split("\n");
831
+ lines.forEach((line, index) => {
832
+ if (line.trim().startsWith("//"))
833
+ return;
834
+ if (line.includes("var(--spacing"))
835
+ return; // Already using tokens
836
+ // Check for numeric margin/padding values > 0.5 (allow 0, 0.5 for small adjustments)
837
+ const spacingMatch = line.match(/\b(m[btlrxy]?|p[btlrxy]?):\s*(\d+\.?\d*)\b/);
838
+ if (spacingMatch) {
839
+ const value = parseFloat(spacingMatch[2]);
840
+ if (value > 0.5) {
841
+ issues.push({
842
+ file,
843
+ line: index + 1,
844
+ type: "numeric-spacing",
845
+ severity: "info",
846
+ message: `Numeric spacing value: ${spacingMatch[1]}: ${value}`,
847
+ suggestion: `Use ${spacingMatch[1]}: "var(--spacing-${Math.round(value * 2)})" for consistency`,
848
+ snippet: line.trim().substring(0, 80),
849
+ });
850
+ }
851
+ }
852
+ // Check for gap values
853
+ const gapMatch = line.match(/\bgap:\s*(\d+\.?\d*)\b/);
854
+ if (gapMatch) {
855
+ const value = parseFloat(gapMatch[1]);
856
+ if (value > 0.5) {
857
+ issues.push({
858
+ file,
859
+ line: index + 1,
860
+ type: "numeric-gap",
861
+ severity: "info",
862
+ message: `Numeric gap value: ${value}`,
863
+ suggestion: `Use gap: "var(--spacing-${Math.round(value * 2)})" for consistency`,
864
+ snippet: line.trim().substring(0, 80),
865
+ });
866
+ }
867
+ }
868
+ });
869
+ }
870
+ return {
871
+ name: "Numeric Spacing",
872
+ passed: true,
873
+ blocking: true,
874
+ issues,
875
+ duration: Date.now() - startTime,
876
+ };
877
+ }
878
+ /**
879
+ * Card Outline Consistency
880
+ * Detects cards/containers using borders instead of shadows for elevation
881
+ */
882
+ async checkCardOutlines() {
883
+ const startTime = Date.now();
884
+ const issues = [];
885
+ const allFiles = await this.getAllFiles();
886
+ // Patterns that indicate a card-like element with a border (problematic)
887
+ const borderPatterns = [
888
+ // Box with border: "1px solid" and borderRadius
889
+ /border:\s*["']1px solid["']/i,
890
+ ];
891
+ // Elements where borders are appropriate (not cards)
892
+ const excludePatterns = [
893
+ /variant=["']outlined["']/i,
894
+ /dashed/i, // Upload zones
895
+ /<TextField/i,
896
+ /<Select/i,
897
+ /<Input/i,
898
+ /<Checkbox/i,
899
+ /<Radio/i,
900
+ /Divider/i,
901
+ /borderTop:/i, // Single-side borders are OK (dividers)
902
+ /borderBottom:/i,
903
+ /borderLeft:/i,
904
+ /borderRight:/i,
905
+ ];
906
+ for (const file of allFiles) {
907
+ const content = fs.readFileSync(file, "utf8");
908
+ const lines = content.split("\n");
909
+ lines.forEach((line, index) => {
910
+ if (line.trim().startsWith("//"))
911
+ return;
912
+ // Check if line has border pattern
913
+ const hasBorder = borderPatterns.some((p) => p.test(line));
914
+ if (!hasBorder)
915
+ return;
916
+ // Skip if it's an excluded element type
917
+ const isExcluded = excludePatterns.some((p) => p.test(line));
918
+ if (isExcluded)
919
+ return;
920
+ // Check surrounding context for card-like indicators
921
+ const contextStart = Math.max(0, index - 5);
922
+ const contextEnd = Math.min(lines.length, index + 5);
923
+ const context = lines.slice(contextStart, contextEnd).join("\n");
924
+ // Only flag if it has significant padding (card-like) and borderRadius
925
+ const hasSignificantPadding = /p:\s*["']var\(--spacing-[4-9]\)|padding.*var\(--spacing-[4-9]\)/i.test(context);
926
+ const hasBorderRadius = /borderRadius/i.test(context);
927
+ if (hasSignificantPadding && hasBorderRadius) {
928
+ issues.push({
929
+ file,
930
+ line: index + 1,
931
+ type: "card-border-instead-of-shadow",
932
+ severity: "warning",
933
+ message: "Card-like element using border instead of shadow",
934
+ suggestion: "Use boxShadow for card elevation instead of border for visual consistency",
935
+ snippet: line.trim().substring(0, 80),
936
+ });
937
+ }
938
+ });
939
+ }
940
+ return {
941
+ name: "Card Outline Consistency",
942
+ passed: true,
943
+ blocking: true,
944
+ issues,
945
+ duration: Date.now() - startTime,
946
+ };
947
+ }
948
+ /**
949
+ * Card Background Check
950
+ * Detects Card components using glass/transparent backgrounds instead of solid backgrounds.
951
+ * Cards should have a solid background (var(--bg-primary) or white) for proper visual hierarchy.
952
+ *
953
+ * ${emoji.error} BAD: background: "var(--glass-bg-medium)" - transparent, hard to see
954
+ * ${emoji.success} GOOD: background: "var(--bg-primary)" - solid white/dark background
955
+ *
956
+ * NOTE: Some glass backgrounds are intentional for design (login forms, loading skeletons, etc.)
957
+ */
958
+ async checkCardBackground() {
959
+ const startTime = Date.now();
960
+ const issues = [];
961
+ const allFiles = await this.getAllFiles();
962
+ // Patterns that indicate transparent/glass backgrounds on cards
963
+ const glassBackgroundPatterns = [
964
+ /background:\s*["']var\(--glass-bg/i,
965
+ /backgroundColor:\s*["']var\(--glass-bg/i,
966
+ /background:\s*["']rgba\([^)]+,\s*0\.[0-3]\)/i, // rgba with very low opacity (0.0-0.3)
967
+ ];
968
+ // Files/patterns where glass backgrounds are intentional
969
+ const excludePatterns = [
970
+ /Modal|Dialog|Drawer/i, // Modals can use glass
971
+ /Overlay/i,
972
+ /Tooltip/i,
973
+ /Popover/i,
974
+ /Header|Nav|Sidebar/i, // Navigation elements
975
+ /backdrop/i,
976
+ /Login|Auth|Register/i, // Auth pages often use glass for design
977
+ /Loading|Skeleton/i, // Loading states
978
+ /Filter|Search/i, // Filter panels
979
+ /Hero|Landing/i, // Landing page sections
980
+ /Faq|Help/i, // Help pages with gradient CTAs
981
+ ];
982
+ for (const file of allFiles) {
983
+ const content = fs.readFileSync(file, "utf8");
984
+ const lines = content.split("\n");
985
+ // Skip files that are intentionally using glass effects
986
+ const fileName = file.split(/[/\\]/).pop() || "";
987
+ if (excludePatterns.some((p) => p.test(fileName)))
988
+ continue;
989
+ lines.forEach((line, index) => {
990
+ if (line.trim().startsWith("//"))
991
+ return;
992
+ // Check if line has glass background pattern
993
+ const hasGlassBackground = glassBackgroundPatterns.some((p) => p.test(line));
994
+ if (!hasGlassBackground)
995
+ return;
996
+ // Check surrounding context for Card component
997
+ const contextStart = Math.max(0, index - 10);
998
+ const contextEnd = Math.min(lines.length, index + 3);
999
+ const context = lines.slice(contextStart, contextEnd).join("\n");
1000
+ // Only flag if it's inside a Card component (not just any Box with glass)
1001
+ const isInsideCard = /<Card[^>]*>/.test(context) &&
1002
+ !/<\/Card>/.test(lines.slice(contextStart, index).join("\n"));
1003
+ // Also check for card-like patterns (MetricCard, StatCard, etc.)
1004
+ const isCardComponent = /Card[^>]*style=\{/.test(context) || /function\s+\w*Card/.test(context);
1005
+ // Skip if this is just an icon container or small decorative element
1006
+ const isIconContainer = /padding:\s*["']var\(--spacing-[23]\)["']/.test(context) &&
1007
+ /Icon|size=\{?\d+\}?|lucide-react/.test(context);
1008
+ const isSmallElement = /width:\s*\d+px|height:\s*\d+px/.test(context) &&
1009
+ parseInt((context.match(/(?:width|height):\s*(\d+)px/) || [])[1] || "100") < 80;
1010
+ const isInlineIconBox = /display:\s*["']inline-flex["']/.test(context) &&
1011
+ /borderRadius:\s*["']var\(--radius-full\)["']/.test(context);
1012
+ if ((isInsideCard || isCardComponent) &&
1013
+ !isIconContainer &&
1014
+ !isSmallElement &&
1015
+ !isInlineIconBox) {
1016
+ issues.push({
1017
+ file,
1018
+ line: index + 1,
1019
+ type: "card-glass-background",
1020
+ severity: "warning",
1021
+ message: "Card using glass/transparent background instead of solid background",
1022
+ suggestion: 'Use background: "var(--bg-primary)" for solid card backgrounds',
1023
+ snippet: line.trim().substring(0, 80),
1024
+ });
1025
+ }
1026
+ });
1027
+ }
1028
+ return {
1029
+ name: "Card Background Check",
1030
+ passed: issues.length === 0,
1031
+ blocking: true,
1032
+ issues,
1033
+ duration: Date.now() - startTime,
1034
+ };
1035
+ }
1036
+ /**
1037
+ * Card Missing Background Check
1038
+ * Detects Card components that don't have a proper solid background.
1039
+ * Cards should always have a visible background for proper visual hierarchy.
1040
+ *
1041
+ * This check catches:
1042
+ * - Cards with transparent/none background
1043
+ * - Cards with only border but no background
1044
+ * - Card-like containers (product cards, item cards) missing backgrounds
1045
+ *
1046
+ * ${emoji.error} BAD: <Card style={{ background: "transparent" }}>
1047
+ * ${emoji.error} BAD: <Card className="bg-transparent">
1048
+ * ${emoji.success} GOOD: <Card> (uses default bg-[var(--bg-primary)])
1049
+ * ${emoji.success} GOOD: <Card className="bg-secondary">
1050
+ */
1051
+ async checkCardMissingBackground() {
1052
+ const startTime = Date.now();
1053
+ const issues = [];
1054
+ const allFiles = await this.getAllFiles();
1055
+ // Patterns that indicate missing/transparent backgrounds
1056
+ const missingBackgroundPatterns = [
1057
+ /background:\s*["'](?:transparent|none)["']/i,
1058
+ /backgroundColor:\s*["'](?:transparent|none)["']/i,
1059
+ /\bbg-transparent\b/,
1060
+ /\bbg-none\b/,
1061
+ ];
1062
+ // Files/patterns where transparent cards might be intentional
1063
+ const excludePatterns = [
1064
+ /Modal|Dialog|Drawer/i,
1065
+ /Overlay/i,
1066
+ /Tooltip/i,
1067
+ /Popover/i,
1068
+ /Skeleton/i,
1069
+ /Ghost/i, // Ghost buttons/cards
1070
+ /Placeholder/i,
1071
+ ];
1072
+ for (const file of allFiles) {
1073
+ const content = fs.readFileSync(file, "utf8");
1074
+ const lines = content.split("\n");
1075
+ // Skip files that are intentionally using transparent effects
1076
+ const fileName = file.split(/[/\\]/).pop() || "";
1077
+ if (excludePatterns.some((p) => p.test(fileName)))
1078
+ continue;
1079
+ lines.forEach((line, index) => {
1080
+ if (line.trim().startsWith("//"))
1081
+ return;
1082
+ // Check if line has transparent background pattern
1083
+ const hasMissingBackground = missingBackgroundPatterns.some((p) => p.test(line));
1084
+ if (!hasMissingBackground)
1085
+ return;
1086
+ // Check surrounding context for Card component
1087
+ const contextStart = Math.max(0, index - 10);
1088
+ const contextEnd = Math.min(lines.length, index + 3);
1089
+ const context = lines.slice(contextStart, contextEnd).join("\n");
1090
+ // Only flag if it's inside a Card component
1091
+ const isInsideCard = /<Card[^>]*>/.test(context) &&
1092
+ !/<\/Card>/.test(lines.slice(contextStart, index).join("\n"));
1093
+ const isCardComponent = /Card[^>]*(?:style=\{|className=)/.test(context) || /function\s+\w*Card/.test(context);
1094
+ // Also check for card-like patterns in class names
1095
+ const isCardLikeElement = /similar-item-card|product-card|listing-card|item-card/i.test(context);
1096
+ if (isInsideCard || isCardComponent || isCardLikeElement) {
1097
+ issues.push({
1098
+ file,
1099
+ line: index + 1,
1100
+ type: "card-missing-background",
1101
+ severity: "warning",
1102
+ message: "Card using transparent/none background - cards should have solid backgrounds",
1103
+ suggestion: "Remove transparent background or use bg-secondary/bg-[var(--bg-primary)] for proper visual hierarchy",
1104
+ snippet: line.trim().substring(0, 80),
1105
+ });
1106
+ }
1107
+ });
1108
+ }
1109
+ return {
1110
+ name: "Card Missing Background Check",
1111
+ passed: issues.length === 0,
1112
+ blocking: false, // Warning for now
1113
+ issues,
1114
+ duration: Date.now() - startTime,
1115
+ };
1116
+ }
1117
+ /**
1118
+ * Deprecated System Components
1119
+ * Detects usage of legacy System components that should be migrated to @supercatch/ui
1120
+ */
1121
+ async checkDeprecatedComponents() {
1122
+ const startTime = Date.now();
1123
+ const issues = [];
1124
+ const allFiles = await this.getAllFiles();
1125
+ const DEPRECATED_IMPORTS = [
1126
+ {
1127
+ pattern: /@\/components\/ButtonSystem/,
1128
+ name: "ButtonSystem",
1129
+ replacement: "Button from @/components/ui",
1130
+ },
1131
+ {
1132
+ pattern: /@\/components\/FlexSystem/,
1133
+ name: "FlexSystem",
1134
+ replacement: "Box or Stack from @/components/ui",
1135
+ },
1136
+ {
1137
+ pattern: /@\/components\/CardSystem/,
1138
+ name: "CardSystem",
1139
+ replacement: "Card/CardContent from @/components/ui",
1140
+ },
1141
+ {
1142
+ pattern: /@\/components\/GridSystem/,
1143
+ name: "GridSystem",
1144
+ replacement: "Box with CSS Grid from @/components/ui",
1145
+ },
1146
+ {
1147
+ pattern: /@\/components\/FeedbackSystem/,
1148
+ name: "FeedbackSystem",
1149
+ replacement: "Alert/Chip from @/components/ui",
1150
+ },
1151
+ ];
1152
+ for (const file of allFiles) {
1153
+ const content = fs.readFileSync(file, "utf8");
1154
+ const lines = content.split("\n");
1155
+ lines.forEach((line, index) => {
1156
+ for (const deprecated of DEPRECATED_IMPORTS) {
1157
+ if (deprecated.pattern.test(line)) {
1158
+ issues.push({
1159
+ file,
1160
+ line: index + 1,
1161
+ type: "deprecated-system-component",
1162
+ severity: "warning",
1163
+ message: `Deprecated ${deprecated.name} import`,
1164
+ suggestion: `Migrate to ${deprecated.replacement}. See docs/SYSTEM-COMPONENTS-MIGRATION.md`,
1165
+ snippet: line.trim().substring(0, 80),
1166
+ });
1167
+ }
1168
+ }
1169
+ });
1170
+ }
1171
+ return {
1172
+ name: "Deprecated System Components",
1173
+ passed: issues.length === 0,
1174
+ blocking: true, // Warning only for now, can be made blocking later
1175
+ issues,
1176
+ duration: Date.now() - startTime,
1177
+ };
1178
+ }
1179
+ /**
1180
+ * Custom Stat Display Detection
1181
+ *
1182
+ * Detects custom stat/metric displays that should use StatCardGrid instead.
1183
+ *
1184
+ * Patterns detected:
1185
+ * - Avatar + Typography (h4/h5/h6) in Card = custom stat display
1186
+ * - Grid of Cards with Avatar + number = custom stat grid
1187
+ * - Box with icon + Typography value pattern
1188
+ *
1189
+ * These should use StatCardGrid for consistency.
1190
+ */
1191
+ async checkCustomStatDisplays() {
1192
+ const startTime = Date.now();
1193
+ const issues = [];
1194
+ const allFiles = await this.getAllFiles();
1195
+ // Skip files that already use StatCardGrid
1196
+ const SKIP_PATTERNS = [
1197
+ /StatCardGrid/,
1198
+ /StatCard/,
1199
+ /\.stories\./,
1200
+ /\.test\./,
1201
+ /\.spec\./,
1202
+ /packages\/ui/,
1203
+ ];
1204
+ // Files that are allowed to have custom stat displays (the StatCard component itself, etc.)
1205
+ const ALLOWED_FILES = [
1206
+ "StatCard.tsx",
1207
+ "StatCard.examples.tsx",
1208
+ "MetricCard.tsx",
1209
+ "KpiCard.tsx",
1210
+ ];
1211
+ for (const file of allFiles) {
1212
+ const normalizedFile = file.replace(/\\/g, "/");
1213
+ // Skip allowed files
1214
+ if (ALLOWED_FILES.some((allowed) => normalizedFile.endsWith(allowed)))
1215
+ continue;
1216
+ const content = fs.readFileSync(file, "utf8");
1217
+ // Skip if file already uses StatCardGrid
1218
+ if (SKIP_PATTERNS.some((p) => p.test(content)))
1219
+ continue;
1220
+ const lines = content.split("\n");
1221
+ // Pattern 1: Avatar inside Card with Typography h4/h5/h6 (stat value pattern)
1222
+ // This is the pattern we found in the image-mapping page
1223
+ for (let i = 0; i < lines.length; i++) {
1224
+ const line = lines[i];
1225
+ // Look for Card opening
1226
+ if (/<Card[\s>]/.test(line) || /<Card\s+/.test(line)) {
1227
+ // Get the next 30 lines to check the Card content
1228
+ const cardBlock = lines.slice(i, Math.min(i + 30, lines.length)).join("\n");
1229
+ // Check for Avatar + Typography with large variant (stat display pattern)
1230
+ const hasAvatar = /<Avatar/.test(cardBlock);
1231
+ const hasStatTypography = /variant=["'](h4|h5)["']/.test(cardBlock);
1232
+ const hasValuePattern = /\.toLocaleString|formatCurrency|formatNumber|formatFileSize/.test(cardBlock);
1233
+ // Check for Stack direction="row" with Avatar (horizontal stat layout)
1234
+ const hasHorizontalStatLayout = /<Stack[^>]*direction=["']row["'][^>]*>[\s\S]*?<Avatar/.test(cardBlock);
1235
+ // Check if this looks like a stat card (Avatar + large number)
1236
+ const looksLikeStatCard = hasAvatar && (hasStatTypography || hasValuePattern) && hasHorizontalStatLayout;
1237
+ if (looksLikeStatCard) {
1238
+ // Additional check: is this in a Grid with multiple similar Cards?
1239
+ const surroundingContext = lines
1240
+ .slice(Math.max(0, i - 10), Math.min(lines.length, i + 40))
1241
+ .join("\n");
1242
+ const isInGrid = /<Grid[^>]*container/.test(surroundingContext);
1243
+ const multipleCards = (surroundingContext.match(/<Card[\s>]/g) || []).length >= 3;
1244
+ if (isInGrid && multipleCards) {
1245
+ issues.push({
1246
+ file,
1247
+ line: i + 1,
1248
+ type: "custom-stat-display",
1249
+ severity: "warning",
1250
+ message: "Custom stat display pattern detected (Avatar + Typography in Card grid)",
1251
+ suggestion: "Use StatCardGrid from @/components/shared for consistent stat displays. See AdminAnalyticsClient.tsx for example.",
1252
+ snippet: line.trim().substring(0, 80),
1253
+ });
1254
+ // Skip ahead to avoid duplicate warnings for same grid
1255
+ i += 30;
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1260
+ // Pattern 2: Box with colored background + icon + Typography (inline stat pattern)
1261
+ for (let i = 0; i < lines.length; i++) {
1262
+ const line = lines[i];
1263
+ // Look for Box with background color containing an icon
1264
+ if (/<Box[^>]*(?:bg-|backgroundColor)/.test(line) ||
1265
+ (/<Box/.test(line) &&
1266
+ /className=["'][^"']*bg-(?:blue|green|red|yellow|cyan|purple)/.test(line))) {
1267
+ const contextBlock = lines.slice(i, Math.min(i + 15, lines.length)).join("\n");
1268
+ // Check for icon + Typography pattern
1269
+ const hasIcon = /lucide-react|Icon|<[A-Z][a-z]+Icon/.test(contextBlock);
1270
+ const hasTypographyValue = /variant=["'](h4|h5|h6)["']/.test(contextBlock);
1271
+ const hasNumericValue = /\.toLocaleString|formatCurrency|formatNumber|%/.test(contextBlock);
1272
+ if (hasIcon && hasTypographyValue && hasNumericValue) {
1273
+ // Check if this is part of a repeated pattern (multiple stats)
1274
+ const fileContent = content;
1275
+ const similarPatterns = (fileContent.match(/<Box[^>]*(?:bg-|backgroundColor)[^>]*>[\s\S]*?<\/Box>/g) || []).length;
1276
+ if (similarPatterns >= 3) {
1277
+ issues.push({
1278
+ file,
1279
+ line: i + 1,
1280
+ type: "custom-stat-box",
1281
+ severity: "info",
1282
+ message: "Custom stat box pattern detected (colored Box + icon + value)",
1283
+ suggestion: "Consider using StatCardGrid for consistent stat displays across the app.",
1284
+ snippet: line.trim().substring(0, 80),
1285
+ });
1286
+ // Skip ahead
1287
+ i += 15;
1288
+ }
1289
+ }
1290
+ }
1291
+ }
1292
+ // Pattern 3: CardContent with direct stat layout (no CardHeader, just value + label)
1293
+ for (let i = 0; i < lines.length; i++) {
1294
+ const line = lines[i];
1295
+ if (/<CardContent/.test(line)) {
1296
+ const cardContentBlock = lines.slice(i, Math.min(i + 20, lines.length)).join("\n");
1297
+ // Check for stat pattern: Stack with Avatar/icon + Box with h4/h5 + body2
1298
+ const hasStatStack = /<Stack[^>]*direction=["']row["'][^>]*spacing=\{?\d/.test(cardContentBlock);
1299
+ const hasIconOrAvatar = /<Avatar|<[A-Z][a-z]+\s/.test(cardContentBlock);
1300
+ const hasValueLabel = /variant=["'](h4|h5)["'][\s\S]*?variant=["'](body2|caption)["']/.test(cardContentBlock);
1301
+ // Check if this is in a Grid with multiple Cards
1302
+ const surroundingContext = lines
1303
+ .slice(Math.max(0, i - 15), Math.min(lines.length, i + 25))
1304
+ .join("\n");
1305
+ const isInStatGrid = /<Grid[^>]*container[^>]*spacing/.test(surroundingContext);
1306
+ const hasMultipleGridItems = (surroundingContext.match(/<Grid[^>]*xs=\{?12[^>]*sm=\{?6|<Grid[^>]*md=\{?2\.4/g) || [])
1307
+ .length >= 3;
1308
+ if (hasStatStack &&
1309
+ hasIconOrAvatar &&
1310
+ hasValueLabel &&
1311
+ isInStatGrid &&
1312
+ hasMultipleGridItems) {
1313
+ issues.push({
1314
+ file,
1315
+ line: i + 1,
1316
+ type: "custom-stat-card-content",
1317
+ severity: "warning",
1318
+ message: "Custom stat card pattern detected (CardContent with icon + value + label in grid)",
1319
+ suggestion: "Use StatCardGrid from @/components/shared. It provides consistent styling, loading states, and trend indicators.",
1320
+ snippet: line.trim().substring(0, 80),
1321
+ });
1322
+ // Skip the rest of this grid
1323
+ i += 20;
1324
+ }
1325
+ }
1326
+ }
1327
+ }
1328
+ return {
1329
+ name: "Custom Stat Display Detection",
1330
+ passed: true, // Non-blocking, just warnings
1331
+ blocking: false,
1332
+ issues,
1333
+ duration: Date.now() - startTime,
1334
+ };
1335
+ }
1336
+ /**
1337
+ * MetricGrid Glass Variant Check (BLOCKING)
1338
+ *
1339
+ * Detects usage of MetricGrid with variant="glass" which has unreliable border rendering.
1340
+ * Use StatCardGrid instead for consistent KPI/stat displays with proper borders.
1341
+ *
1342
+ * ${emoji.error} BAD: <MetricGrid variant="glass" metrics={...} />
1343
+ * ${emoji.success} GOOD: <StatCardGrid stats={...} />
1344
+ */
1345
+ async checkMetricGridGlassVariant() {
1346
+ const startTime = Date.now();
1347
+ const issues = [];
1348
+ const allFiles = await this.getAllFiles();
1349
+ for (const file of allFiles) {
1350
+ const content = fs.readFileSync(file, "utf8");
1351
+ const lines = content.split("\n");
1352
+ lines.forEach((line, index) => {
1353
+ if (line.trim().startsWith("//"))
1354
+ return;
1355
+ // Check for MetricGrid with glass variant
1356
+ if (/MetricGrid/.test(line) && /variant=["']glass["']/.test(line)) {
1357
+ issues.push({
1358
+ file,
1359
+ line: index + 1,
1360
+ type: "metric-grid-glass-variant",
1361
+ severity: "error",
1362
+ message: 'MetricGrid with variant="glass" has unreliable border rendering',
1363
+ suggestion: "Use StatCardGrid from @/components/shared instead. It uses the Card component with proper borders.",
1364
+ snippet: line.trim().substring(0, 100),
1365
+ });
1366
+ }
1367
+ // Also check for multi-line MetricGrid with glass variant
1368
+ if (/MetricGrid/.test(line) && !/>/.test(line)) {
1369
+ const contextEnd = Math.min(lines.length, index + 5);
1370
+ const context = lines.slice(index, contextEnd).join("\n");
1371
+ if (/variant=["']glass["']/.test(context)) {
1372
+ issues.push({
1373
+ file,
1374
+ line: index + 1,
1375
+ type: "metric-grid-glass-variant",
1376
+ severity: "error",
1377
+ message: 'MetricGrid with variant="glass" has unreliable border rendering',
1378
+ suggestion: "Use StatCardGrid from @/components/shared instead. It uses the Card component with proper borders.",
1379
+ snippet: line.trim().substring(0, 100),
1380
+ });
1381
+ }
1382
+ }
1383
+ });
1384
+ }
1385
+ const hasErrors = issues.some((i) => i.severity === "error");
1386
+ return {
1387
+ name: "MetricGrid Glass Variant Check",
1388
+ passed: !hasErrors,
1389
+ blocking: true,
1390
+ issues,
1391
+ duration: Date.now() - startTime,
1392
+ };
1393
+ }
1394
+ /**
1395
+ * Chained CSS Variable Check (BLOCKING)
1396
+ *
1397
+ * Detects usage of CSS variables that chain through intermediate variables,
1398
+ * which can fail to resolve in inline styles.
1399
+ *
1400
+ * ${emoji.error} BAD: border: "1px solid var(--glass-surface-border)" (chains to --border-color)
1401
+ * ${emoji.success} GOOD: border: "1px solid var(--border-color)" (direct variable)
1402
+ *
1403
+ * Known problematic variables:
1404
+ * - --glass-surface-border (chains to --border-color)
1405
+ * - --glass-border-* (may not resolve in all contexts)
1406
+ */
1407
+ async checkChainedCssVariables() {
1408
+ const startTime = Date.now();
1409
+ const issues = [];
1410
+ const allFiles = await this.getAllFiles();
1411
+ // Variables that chain through intermediate variables and may not resolve
1412
+ const PROBLEMATIC_VARIABLES = [
1413
+ {
1414
+ pattern: /var\(--glass-surface-border\)/,
1415
+ name: "--glass-surface-border",
1416
+ replacement: "--border-color",
1417
+ },
1418
+ {
1419
+ pattern: /var\(--glass-border-light\)/,
1420
+ name: "--glass-border-light",
1421
+ replacement: "--border-color",
1422
+ },
1423
+ {
1424
+ pattern: /var\(--glass-border-medium\)/,
1425
+ name: "--glass-border-medium",
1426
+ replacement: "--border-color",
1427
+ },
1428
+ {
1429
+ pattern: /var\(--glass-border-strong\)/,
1430
+ name: "--glass-border-strong",
1431
+ replacement: "--border-color",
1432
+ },
1433
+ ];
1434
+ for (const file of allFiles) {
1435
+ // Skip CSS files - these define the variables
1436
+ if (file.endsWith(".css"))
1437
+ continue;
1438
+ const content = fs.readFileSync(file, "utf8");
1439
+ const lines = content.split("\n");
1440
+ lines.forEach((line, index) => {
1441
+ if (line.trim().startsWith("//"))
1442
+ return;
1443
+ // Check for problematic variables in inline styles
1444
+ for (const variable of PROBLEMATIC_VARIABLES) {
1445
+ if (variable.pattern.test(line)) {
1446
+ // Only flag if this is in a style prop (inline style)
1447
+ const isInlineStyle = /style=\{|style:\s*\{/.test(line) ||
1448
+ (index > 0 && /style=\{/.test(lines.slice(Math.max(0, index - 5), index).join("\n")));
1449
+ if (isInlineStyle) {
1450
+ issues.push({
1451
+ file,
1452
+ line: index + 1,
1453
+ type: "chained-css-variable",
1454
+ severity: "error",
1455
+ message: `Chained CSS variable ${variable.name} may not resolve in inline styles`,
1456
+ suggestion: `Use var(${variable.replacement}) directly instead`,
1457
+ snippet: line.trim().substring(0, 100),
1458
+ });
1459
+ }
1460
+ }
1461
+ }
1462
+ });
1463
+ }
1464
+ const hasErrors = issues.some((i) => i.severity === "error");
1465
+ return {
1466
+ name: "Chained CSS Variable Check",
1467
+ passed: !hasErrors,
1468
+ blocking: true,
1469
+ issues,
1470
+ duration: Date.now() - startTime,
1471
+ };
1472
+ }
1473
+ /**
1474
+ * Duplicate className Props Check (BLOCKING)
1475
+ *
1476
+ * Detects JSX elements with multiple className props, where the second one
1477
+ * overwrites the first, causing styles to be lost.
1478
+ *
1479
+ * ${emoji.error} BAD: <Box className="p-4" onClick={...} className="hover:shadow-lg" />
1480
+ * ${emoji.success} GOOD: <Box className="p-4 hover:shadow-lg" onClick={...} />
1481
+ * ${emoji.success} GOOD: <Box className={`p-4 ${onClick ? "hover:shadow-lg" : ""}`} />
1482
+ */
1483
+ async checkDuplicateClassNameProps() {
1484
+ const startTime = Date.now();
1485
+ const issues = [];
1486
+ const allFiles = await this.getAllFiles();
1487
+ for (const file of allFiles) {
1488
+ const content = fs.readFileSync(file, "utf8");
1489
+ const lines = content.split("\n");
1490
+ // Track open JSX elements across lines - only track the opening tag, not children
1491
+ let inOpeningTag = false;
1492
+ let tagStartLine = 0;
1493
+ let classNameCount = 0;
1494
+ let tagContent = "";
1495
+ lines.forEach((line, index) => {
1496
+ if (line.trim().startsWith("//"))
1497
+ return;
1498
+ // Simple single-line check - only count className= that are direct props on same element
1499
+ // Look for pattern: <Element className="..." ... className="...">
1500
+ const singleLineMatch = line.match(/<[A-Za-z][A-Za-z0-9]*[^>]*>/g);
1501
+ if (singleLineMatch) {
1502
+ for (const tag of singleLineMatch) {
1503
+ const classNameMatches = tag.match(/\bclassName=/g);
1504
+ if (classNameMatches && classNameMatches.length > 1) {
1505
+ issues.push({
1506
+ file,
1507
+ line: index + 1,
1508
+ type: "duplicate-classname-prop",
1509
+ severity: "error",
1510
+ message: "Multiple className props on same element - second one overwrites first",
1511
+ suggestion: "Merge className values into a single prop using template literals or cn() utility",
1512
+ snippet: line.trim().substring(0, 100),
1513
+ });
1514
+ }
1515
+ }
1516
+ }
1517
+ // Multi-line JSX opening tag tracking
1518
+ // Start tracking when we see <Component (uppercase) without closing >
1519
+ if (!inOpeningTag &&
1520
+ /<[A-Z][a-zA-Z0-9]*\s/.test(line) &&
1521
+ !/>/.test(line.split(/<[A-Z][a-zA-Z0-9]*/)[1] || "")) {
1522
+ inOpeningTag = true;
1523
+ tagStartLine = index;
1524
+ tagContent = line;
1525
+ classNameCount = (line.match(/\bclassName=/g) || []).length;
1526
+ }
1527
+ else if (inOpeningTag) {
1528
+ tagContent += "\n" + line;
1529
+ // Only count className= if this line is still part of the opening tag props
1530
+ // (before the first > that closes the opening tag)
1531
+ const beforeClose = line.split(">")[0];
1532
+ // Don't count if this line starts a nested element
1533
+ if (!/<[A-Za-z]/.test(beforeClose)) {
1534
+ classNameCount += (beforeClose.match(/\bclassName=/g) || []).length;
1535
+ }
1536
+ // Check if opening tag closes on this line
1537
+ if (/>/.test(line)) {
1538
+ // Opening tag closed
1539
+ if (classNameCount > 1) {
1540
+ issues.push({
1541
+ file,
1542
+ line: tagStartLine + 1,
1543
+ type: "duplicate-classname-prop",
1544
+ severity: "error",
1545
+ message: "Multiple className props on same element - second one overwrites first",
1546
+ suggestion: "Merge className values into a single prop using template literals or cn() utility",
1547
+ snippet: lines[tagStartLine].trim().substring(0, 100),
1548
+ });
1549
+ }
1550
+ inOpeningTag = false;
1551
+ classNameCount = 0;
1552
+ tagContent = "";
1553
+ }
1554
+ }
1555
+ });
1556
+ }
1557
+ const hasErrors = issues.some((i) => i.severity === "error");
1558
+ return {
1559
+ name: "Duplicate className Props Check",
1560
+ passed: !hasErrors,
1561
+ blocking: true,
1562
+ issues,
1563
+ duration: Date.now() - startTime,
1564
+ };
1565
+ }
1566
+ /**
1567
+ * Hardcoded Colors
1568
+ * Detects internal colors that should use CSS variables (allows platform brand colors)
1569
+ */
1570
+ async checkHardcodedColors() {
1571
+ const startTime = Date.now();
1572
+ const issues = [];
1573
+ const allFiles = await this.getAllFiles();
1574
+ for (const file of allFiles) {
1575
+ // Skip platform icon files
1576
+ if (file.includes("PlatformIcon"))
1577
+ continue;
1578
+ const content = fs.readFileSync(file, "utf8");
1579
+ const lines = content.split("\n");
1580
+ lines.forEach((line, index) => {
1581
+ if (line.trim().startsWith("//"))
1582
+ return;
1583
+ if (line.includes("var(--color-"))
1584
+ return; // Already using tokens
1585
+ // Check for hex colors
1586
+ const hexMatches = line.matchAll(/#([0-9a-fA-F]{3,6})\b/g);
1587
+ for (const match of hexMatches) {
1588
+ const color = `#${match[1]}`.toLowerCase();
1589
+ // Skip platform brand colors (OK to hardcode)
1590
+ if (PLATFORM_BRAND_COLORS.includes(color))
1591
+ continue;
1592
+ // Skip common values
1593
+ if (["#fff", "#ffffff", "#000", "#000000"].includes(color))
1594
+ continue;
1595
+ // Skip primary brand color used in metadata (themeColor, msapplication-TileColor)
1596
+ // These cannot use CSS variables as browsers parse them before stylesheets load
1597
+ if (["#1e63f1"].includes(color))
1598
+ continue;
1599
+ // Check context - is this in a styling property?
1600
+ if (/(color|background|bg|border|fill|stroke)/.test(line.toLowerCase())) {
1601
+ issues.push({
1602
+ file,
1603
+ line: index + 1,
1604
+ type: "hardcoded-color",
1605
+ severity: "info",
1606
+ message: `Hardcoded color: ${color}`,
1607
+ suggestion: "Use var(--color-{name}) or var(--text-{name}) for consistency",
1608
+ snippet: line.trim().substring(0, 80),
1609
+ });
1610
+ }
1611
+ }
1612
+ });
1613
+ }
1614
+ return {
1615
+ name: "Hardcoded Colors",
1616
+ passed: true,
1617
+ blocking: true,
1618
+ issues,
1619
+ duration: Date.now() - startTime,
1620
+ };
1621
+ }
1622
+ /**
1623
+ * Autocomplete UX Guardrails (BLOCKING)
1624
+ *
1625
+ * Enforce consistent Autocomplete UX across the site:
1626
+ * - Disallow `floatingLabel={false}` (we use label-in-border globally)
1627
+ * - Disallow `openOnFocus={true}` (can create unexpected large dropdown panels)
1628
+ * - Disallow manual <label> directly above an <Autocomplete>
1629
+ */
1630
+ async checkAutocompleteUX() {
1631
+ const startTime = Date.now();
1632
+ const issues = [];
1633
+ const files = await getAppComponentsTsxFiles();
1634
+ for (const file of files) {
1635
+ const normalizedFile = file.replace(/\\/g, "/");
1636
+ // Ignore UI primitive implementation itself
1637
+ if (normalizedFile.startsWith("components/ui/"))
1638
+ continue;
1639
+ const content = fs.readFileSync(file, "utf8");
1640
+ if (!content.includes("Autocomplete") && !content.includes("<label"))
1641
+ continue;
1642
+ const lines = content.split("\n");
1643
+ // 1) Disallow opt-out of floating labels
1644
+ lines.forEach((line, index) => {
1645
+ if (/floatingLabel\s*=\s*\{?\s*false\s*\}?/.test(line)) {
1646
+ issues.push({
1647
+ file,
1648
+ line: index + 1,
1649
+ type: "autocomplete-floatinglabel-disabled",
1650
+ severity: "error",
1651
+ message: "Autocomplete floating labels must remain enabled (no floatingLabel={false})",
1652
+ suggestion: "Remove `floatingLabel={false}` and rely on the shared Autocomplete default.",
1653
+ snippet: line.trim().substring(0, 140),
1654
+ });
1655
+ }
1656
+ if (/openOnFocus\s*=\s*\{?\s*true\s*\}?/.test(line)) {
1657
+ issues.push({
1658
+ file,
1659
+ line: index + 1,
1660
+ type: "autocomplete-openonfocus-enabled",
1661
+ severity: "error",
1662
+ message: "Autocomplete should not auto-open on focus (openOnFocus={true} is disallowed)",
1663
+ suggestion: "Remove `openOnFocus` (default is false) and rely on click-to-open behavior.",
1664
+ snippet: line.trim().substring(0, 140),
1665
+ });
1666
+ }
1667
+ });
1668
+ // 2) Disallow manual <label> directly above <Autocomplete>
1669
+ for (let i = 0; i < lines.length; i++) {
1670
+ const line = lines[i];
1671
+ if (!/<label\b/i.test(line))
1672
+ continue;
1673
+ if (line.trim().startsWith("//"))
1674
+ continue;
1675
+ // Look ahead for the next meaningful line
1676
+ for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
1677
+ const next = lines[j].trim();
1678
+ if (!next || next.startsWith("//"))
1679
+ continue;
1680
+ if (next.startsWith("{/*") || next.startsWith("*/}"))
1681
+ continue;
1682
+ if (/<Autocomplete\b/.test(next)) {
1683
+ issues.push({
1684
+ file,
1685
+ line: i + 1,
1686
+ type: "autocomplete-manual-label",
1687
+ severity: "error",
1688
+ message: "Manual <label> directly above <Autocomplete> is not allowed (use Autocomplete label prop only)",
1689
+ suggestion: "Remove the manual <label> and pass its text via the Autocomplete `label` prop.",
1690
+ snippet: line.trim().substring(0, 140),
1691
+ });
1692
+ }
1693
+ break;
1694
+ }
1695
+ }
1696
+ }
1697
+ const hasErrors = issues.some((i) => i.severity === "error");
1698
+ return {
1699
+ name: "Autocomplete UX Guardrails",
1700
+ passed: !hasErrors,
1701
+ blocking: true,
1702
+ issues,
1703
+ duration: Date.now() - startTime,
1704
+ };
1705
+ }
1706
+ /**
1707
+ * Autocomplete Implementation Guardrails (BLOCKING)
1708
+ *
1709
+ * Ensure the shared Autocomplete keeps the runtime protections that prevent
1710
+ * conditional-mount click-through from opening the dropdown and visually
1711
+ * "whiting out" large parts of the page.
1712
+ */
1713
+ async checkAutocompleteImplementation() {
1714
+ const startTime = Date.now();
1715
+ const issues = [];
1716
+ const file = "components/ui/Autocomplete.tsx";
1717
+ try {
1718
+ const content = fs.readFileSync(file, "utf8");
1719
+ const requiredSnippets = [
1720
+ { needle: /openOnFocus\s*=\s*false/, label: "default openOnFocus=false" },
1721
+ {
1722
+ needle: /onPointerDown=\{\(\)\s*=>\s*\{\s*\n?\s*lastPointerDownAtRef\.current\s*=\s*Date\.now\(\)/m,
1723
+ label: "pointerdown gating for click-to-open",
1724
+ },
1725
+ {
1726
+ needle: /ignoreClicksUntilRef\s*=\s*useRef<\s*number\s*>\(\s*Date\.now\(\)\s*\+\s*\d+/m,
1727
+ label: "initial click-through ignore window",
1728
+ },
1729
+ ];
1730
+ for (const req of requiredSnippets) {
1731
+ if (!req.needle.test(content)) {
1732
+ issues.push({
1733
+ file,
1734
+ line: 1,
1735
+ type: "autocomplete-implementation-guard",
1736
+ severity: "error",
1737
+ message: `Shared Autocomplete missing required guardrail: ${req.label}`,
1738
+ suggestion: "Do not remove the click-through protections; they prevent the shipping page from being visually obscured when toggling features.",
1739
+ });
1740
+ }
1741
+ }
1742
+ }
1743
+ catch {
1744
+ issues.push({
1745
+ file,
1746
+ line: 1,
1747
+ type: "autocomplete-implementation-guard",
1748
+ severity: "error",
1749
+ message: "Could not read shared Autocomplete implementation to validate click-through protections.",
1750
+ });
1751
+ }
1752
+ const hasErrors = issues.some((i) => i.severity === "error");
1753
+ return {
1754
+ name: "Autocomplete Implementation Guardrails",
1755
+ passed: !hasErrors,
1756
+ blocking: true,
1757
+ issues,
1758
+ duration: Date.now() - startTime,
1759
+ };
1760
+ }
1761
+ async runAll() {
1762
+ const startTime = Date.now();
1763
+ console.log("\n🧩 CONSOLIDATED UI COMPONENTS PREFLIGHT");
1764
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
1765
+ const checks = [];
1766
+ checks.push(await this.checkButtons());
1767
+ checks.push(await this.checkLinkButtons());
1768
+ checks.push(await this.checkClickableNonButtons());
1769
+ checks.push(await this.checkIconButtonAccessibility());
1770
+ checks.push(await this.checkBadges());
1771
+ checks.push(await this.checkIcons());
1772
+ checks.push(await this.checkBorderRadius());
1773
+ checks.push(await this.checkShadows());
1774
+ checks.push(await this.checkChips());
1775
+ checks.push(await this.checkPlatformIcons());
1776
+ checks.push(await this.checkNumericSpacing());
1777
+ checks.push(await this.checkHardcodedColors());
1778
+ checks.push(await this.checkAutocompleteUX());
1779
+ checks.push(await this.checkAutocompleteImplementation());
1780
+ checks.push(await this.checkCardOutlines());
1781
+ checks.push(await this.checkCardBackground());
1782
+ checks.push(await this.checkCardMissingBackground());
1783
+ checks.push(await this.checkMetricGridGlassVariant());
1784
+ checks.push(await this.checkChainedCssVariables());
1785
+ checks.push(await this.checkDuplicateClassNameProps());
1786
+ checks.push(await this.checkUnifiedDialogPadding());
1787
+ checks.push(await this.checkDeprecatedComponents());
1788
+ checks.push(await this.checkCustomStatDisplays());
1789
+ const totalDuration = Date.now() - startTime;
1790
+ const allIssues = checks.flatMap((c) => c.issues);
1791
+ const summary = {
1792
+ total: checks.length,
1793
+ passed: checks.filter((c) => c.passed).length,
1794
+ failed: checks.filter((c) => !c.passed).length,
1795
+ errors: allIssues.filter((i) => i.severity === "error").length,
1796
+ warnings: allIssues.filter((i) => i.severity === "warning").length,
1797
+ info: allIssues.filter((i) => i.severity === "info").length,
1798
+ };
1799
+ this.printResults(checks, totalDuration, summary);
1800
+ return {
1801
+ module: "ui-components",
1802
+ passed: true,
1803
+ totalDuration,
1804
+ checks,
1805
+ summary,
1806
+ };
1807
+ }
1808
+ printResults(checks, totalDuration, summary) {
1809
+ console.log("\n" + (0, console_chars_1.createDivider)(80, "heavy"));
1810
+ console.log(`${console_chars_1.emoji.chart} UI COMPONENTS RESULTS`);
1811
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
1812
+ checks.forEach((check) => {
1813
+ const icon = check.issues.length === 0 ? `${console_chars_1.emoji.success}` : `${console_chars_1.emoji.warning}`;
1814
+ const count = check.issues.length > 0 ? ` (${check.issues.length} issues)` : "";
1815
+ console.log(`${icon} ${check.name.padEnd(30)} ${(check.duration / 1000).toFixed(1)}s${count}`);
1816
+ });
1817
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
1818
+ console.log(`${console_chars_1.emoji.warning} Warnings: ${summary.warnings} | ${console_chars_1.emoji.info} Info: ${summary.info} | ${console_chars_1.emoji.clock} Time: ${(totalDuration / 1000).toFixed(1)}s`);
1819
+ // Print detailed issues if verbose mode or if there are warnings/errors
1820
+ const allIssues = checks.flatMap((c) => c.issues);
1821
+ const warningsAndErrors = allIssues.filter((i) => i.severity === "warning" || i.severity === "error");
1822
+ if (this.verbose || warningsAndErrors.length > 0) {
1823
+ console.log(`\n${console_chars_1.emoji.clipboard} DETAILED ISSUES:`);
1824
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
1825
+ checks.forEach((check) => {
1826
+ if (check.issues.length > 0) {
1827
+ console.log(`\n${console_chars_1.emoji.search} ${check.name}:`);
1828
+ check.issues.forEach((issue) => {
1829
+ const icon = issue.severity === "error"
1830
+ ? `${console_chars_1.emoji.error}`
1831
+ : issue.severity === "warning"
1832
+ ? `${console_chars_1.emoji.warning}`
1833
+ : `${console_chars_1.emoji.info}`;
1834
+ const relPath = issue.file.replace(/\\/g, "/");
1835
+ console.log(` ${icon} ${relPath}:${issue.line}`);
1836
+ console.log(` ${issue.message}`);
1837
+ if (issue.suggestion) {
1838
+ console.log(` ${console_chars_1.emoji.hint} ${issue.suggestion}`);
1839
+ }
1840
+ });
1841
+ }
1842
+ });
1843
+ }
1844
+ console.log(`\n${console_chars_1.emoji.success} UI COMPONENTS PASSED (non-blocking warnings)`);
1845
+ }
1846
+ shouldFailInStrictMode(checks) {
1847
+ if (!this.strictUniformity)
1848
+ return false;
1849
+ return checks.some((c) => c.issues.some((i) => i.severity === "warning" || i.severity === "error"));
1850
+ }
1851
+ }
1852
+ exports.UIComponentsPreflightModule = UIComponentsPreflightModule;
1853
+ async function main() {
1854
+ appExclusions = await (0, exclusions_1.getExclusions)(id);
1855
+ const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(path.basename(__filename, ".ts"));
1856
+ const args = process.argv.slice(2);
1857
+ const mode = args[0] || "all";
1858
+ const verbose = args.includes("--verbose") || args.includes("-v");
1859
+ const parallel = args.includes("--parallel") || args.includes("-p");
1860
+ const module = new UIComponentsPreflightModule({ verbose, parallel });
1861
+ const runSingle = async (name, fn) => {
1862
+ const check = await fn();
1863
+ // Ensure subset modes still print a useful report
1864
+ const summary = {
1865
+ total: 1,
1866
+ passed: 1,
1867
+ failed: 0,
1868
+ errors: check.issues.filter((i) => i.severity === "error").length,
1869
+ warnings: check.issues.filter((i) => i.severity === "warning").length,
1870
+ info: check.issues.filter((i) => i.severity === "info").length,
1871
+ };
1872
+ module.printResults([check], check.duration, summary);
1873
+ return {
1874
+ module: `ui-components:${name}`,
1875
+ passed: true,
1876
+ totalDuration: check.duration,
1877
+ checks: [check],
1878
+ summary: {
1879
+ total: 1,
1880
+ passed: 1,
1881
+ failed: 0,
1882
+ errors: 0,
1883
+ warnings: check.issues.filter((i) => i.severity === "warning").length,
1884
+ info: check.issues.filter((i) => i.severity === "info").length,
1885
+ },
1886
+ };
1887
+ };
1888
+ let result;
1889
+ switch (mode) {
1890
+ case "buttons":
1891
+ result = await runSingle("buttons", () => module.checkButtons());
1892
+ break;
1893
+ case "links":
1894
+ result = await runSingle("links", () => module.checkLinkButtons());
1895
+ break;
1896
+ case "clickables":
1897
+ result = await runSingle("clickables", () => module.checkClickableNonButtons());
1898
+ break;
1899
+ case "iconbuttons":
1900
+ result = await runSingle("iconbuttons", () => module.checkIconButtonAccessibility());
1901
+ break;
1902
+ case "uniformity":
1903
+ // Uniformity mode: highest-signal checks that prevent UI drift.
1904
+ // Kept warning-only by default; can be made strict with PREFLIGHT_STRICT_UNIFORMITY=1.
1905
+ console.log("\n🧭 UI UNIFORMITY (subset)");
1906
+ console.log((0, console_chars_1.createDivider)(80, "heavy"));
1907
+ const uniformityChecksPromises = [
1908
+ module.checkButtons(),
1909
+ module.checkLinkButtons(),
1910
+ module.checkClickableNonButtons(),
1911
+ module.checkIconButtonAccessibility(),
1912
+ module.checkUnifiedDialogPadding(),
1913
+ ];
1914
+ const uniformityChecks = await Promise.all(uniformityChecksPromises);
1915
+ module.printResults(uniformityChecks, uniformityChecks.reduce((sum, c) => sum + c.duration, 0), {
1916
+ total: uniformityChecks.length,
1917
+ passed: uniformityChecks.length,
1918
+ failed: 0,
1919
+ errors: uniformityChecks.flatMap((c) => c.issues).filter((i) => i.severity === "error")
1920
+ .length,
1921
+ warnings: uniformityChecks
1922
+ .flatMap((c) => c.issues)
1923
+ .filter((i) => i.severity === "warning").length,
1924
+ info: uniformityChecks.flatMap((c) => c.issues).filter((i) => i.severity === "info")
1925
+ .length,
1926
+ });
1927
+ result = {
1928
+ module: "ui-components:uniformity",
1929
+ passed: true,
1930
+ totalDuration: uniformityChecks.reduce((sum, c) => sum + c.duration, 0),
1931
+ checks: uniformityChecks,
1932
+ summary: {
1933
+ total: uniformityChecks.length,
1934
+ passed: uniformityChecks.length,
1935
+ failed: 0,
1936
+ errors: 0,
1937
+ warnings: uniformityChecks
1938
+ .flatMap((c) => c.issues)
1939
+ .filter((i) => i.severity === "warning").length,
1940
+ info: uniformityChecks.flatMap((c) => c.issues).filter((i) => i.severity === "info")
1941
+ .length,
1942
+ },
1943
+ };
1944
+ break;
1945
+ case "badges":
1946
+ result = await runSingle("badges", () => module.checkBadges());
1947
+ break;
1948
+ case "icons":
1949
+ result = await runSingle("icons", () => module.checkIcons());
1950
+ break;
1951
+ case "radius":
1952
+ result = await runSingle("radius", () => module.checkBorderRadius());
1953
+ break;
1954
+ case "shadows":
1955
+ result = await runSingle("shadows", () => module.checkShadows());
1956
+ break;
1957
+ case "chips":
1958
+ result = await runSingle("chips", () => module.checkChips());
1959
+ break;
1960
+ case "platforms":
1961
+ result = await runSingle("platforms", () => module.checkPlatformIcons());
1962
+ break;
1963
+ case "spacing":
1964
+ result = await runSingle("spacing", () => module.checkNumericSpacing());
1965
+ break;
1966
+ case "colors":
1967
+ result = await runSingle("colors", () => module.checkHardcodedColors());
1968
+ break;
1969
+ case "cards":
1970
+ result = await runSingle("cards", () => module.checkCardOutlines());
1971
+ break;
1972
+ case "card-backgrounds":
1973
+ result = await runSingle("card-backgrounds", () => module.checkCardBackground());
1974
+ break;
1975
+ case "card-missing-bg":
1976
+ case "missing-backgrounds":
1977
+ result = await runSingle("card-missing-backgrounds", () => module.checkCardMissingBackground());
1978
+ break;
1979
+ case "metric-grid":
1980
+ case "glass":
1981
+ result = await runSingle("metric-grid-glass", () => module.checkMetricGridGlassVariant());
1982
+ break;
1983
+ case "css-variables":
1984
+ case "chained-vars":
1985
+ result = await runSingle("chained-css-variables", () => module.checkChainedCssVariables());
1986
+ break;
1987
+ case "duplicate-classname":
1988
+ case "classname":
1989
+ result = await runSingle("duplicate-classname", () => module.checkDuplicateClassNameProps());
1990
+ break;
1991
+ case "deprecated":
1992
+ result = await runSingle("deprecated", () => module.checkDeprecatedComponents());
1993
+ break;
1994
+ case "stat-displays":
1995
+ case "stats":
1996
+ result = await runSingle("stat-displays", () => module.checkCustomStatDisplays());
1997
+ break;
1998
+ default:
1999
+ result = await module.runAll();
2000
+ break;
2001
+ }
2002
+ const strictFail = module.shouldFailInStrictMode(result.checks);
2003
+ process.exit(strictFail ? 1 : 0);
2004
+ }
2005
+ if (require.main === module) {
2006
+ main();
2007
+ }
2008
+ //# sourceMappingURL=ui-components.js.map