@sun-asterisk/sungen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +490 -0
  3. package/bin/sungen.js +12 -0
  4. package/dist/cli/commands/auto-tag-command.d.ts +8 -0
  5. package/dist/cli/commands/auto-tag-command.d.ts.map +1 -0
  6. package/dist/cli/commands/auto-tag-command.js +104 -0
  7. package/dist/cli/commands/auto-tag-command.js.map +1 -0
  8. package/dist/cli/index.d.ts +7 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +196 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/config/ai-providers.yaml +56 -0
  13. package/dist/config/config-loader.d.ts +51 -0
  14. package/dist/config/config-loader.d.ts.map +1 -0
  15. package/dist/config/config-loader.js +216 -0
  16. package/dist/config/config-loader.js.map +1 -0
  17. package/dist/config/config-schema.d.ts +121 -0
  18. package/dist/config/config-schema.d.ts.map +1 -0
  19. package/dist/config/config-schema.js +7 -0
  20. package/dist/config/config-schema.js.map +1 -0
  21. package/dist/config/default.config.yaml +101 -0
  22. package/dist/config/framework.config.yaml +52 -0
  23. package/dist/config/routes.yaml +31 -0
  24. package/dist/core/selector-base/annotation-handler.d.ts +45 -0
  25. package/dist/core/selector-base/annotation-handler.d.ts.map +1 -0
  26. package/dist/core/selector-base/annotation-handler.js +102 -0
  27. package/dist/core/selector-base/annotation-handler.js.map +1 -0
  28. package/dist/core/selector-base/base-generator.d.ts +49 -0
  29. package/dist/core/selector-base/base-generator.d.ts.map +1 -0
  30. package/dist/core/selector-base/base-generator.js +214 -0
  31. package/dist/core/selector-base/base-generator.js.map +1 -0
  32. package/dist/core/selector-base/gherkin-parser.d.ts +24 -0
  33. package/dist/core/selector-base/gherkin-parser.d.ts.map +1 -0
  34. package/dist/core/selector-base/gherkin-parser.js +42 -0
  35. package/dist/core/selector-base/gherkin-parser.js.map +1 -0
  36. package/dist/core/selector-mapper/priority-mapper.d.ts +74 -0
  37. package/dist/core/selector-mapper/priority-mapper.d.ts.map +1 -0
  38. package/dist/core/selector-mapper/priority-mapper.js +477 -0
  39. package/dist/core/selector-mapper/priority-mapper.js.map +1 -0
  40. package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts +91 -0
  41. package/dist/core/ui-scanner/heuristics/base-heuristic.d.ts.map +1 -0
  42. package/dist/core/ui-scanner/heuristics/base-heuristic.js +175 -0
  43. package/dist/core/ui-scanner/heuristics/base-heuristic.js.map +1 -0
  44. package/dist/core/ui-scanner/react-scanner.d.ts +32 -0
  45. package/dist/core/ui-scanner/react-scanner.d.ts.map +1 -0
  46. package/dist/core/ui-scanner/react-scanner.js +163 -0
  47. package/dist/core/ui-scanner/react-scanner.js.map +1 -0
  48. package/dist/core/ui-scanner/scanner-interface.d.ts +94 -0
  49. package/dist/core/ui-scanner/scanner-interface.d.ts.map +1 -0
  50. package/dist/core/ui-scanner/scanner-interface.js +33 -0
  51. package/dist/core/ui-scanner/scanner-interface.js.map +1 -0
  52. package/dist/core/ui-scanner/strict-scanner.d.ts +81 -0
  53. package/dist/core/ui-scanner/strict-scanner.d.ts.map +1 -0
  54. package/dist/core/ui-scanner/strict-scanner.js +511 -0
  55. package/dist/core/ui-scanner/strict-scanner.js.map +1 -0
  56. package/dist/executor/playwright/playwright-generator.d.ts +33 -0
  57. package/dist/executor/playwright/playwright-generator.d.ts.map +1 -0
  58. package/dist/executor/playwright/playwright-generator.js +136 -0
  59. package/dist/executor/playwright/playwright-generator.js.map +1 -0
  60. package/dist/executor/test-generator.d.ts +63 -0
  61. package/dist/executor/test-generator.d.ts.map +1 -0
  62. package/dist/executor/test-generator.js +30 -0
  63. package/dist/executor/test-generator.js.map +1 -0
  64. package/dist/external/ai-provider.d.ts +60 -0
  65. package/dist/external/ai-provider.d.ts.map +1 -0
  66. package/dist/external/ai-provider.js +30 -0
  67. package/dist/external/ai-provider.js.map +1 -0
  68. package/dist/external/anthropic-provider.d.ts +29 -0
  69. package/dist/external/anthropic-provider.d.ts.map +1 -0
  70. package/dist/external/anthropic-provider.js +85 -0
  71. package/dist/external/anthropic-provider.js.map +1 -0
  72. package/dist/generators/cache/cache-manager.d.ts +66 -0
  73. package/dist/generators/cache/cache-manager.d.ts.map +1 -0
  74. package/dist/generators/cache/cache-manager.js +286 -0
  75. package/dist/generators/cache/cache-manager.js.map +1 -0
  76. package/dist/generators/cli.d.ts +7 -0
  77. package/dist/generators/cli.d.ts.map +1 -0
  78. package/dist/generators/cli.js +570 -0
  79. package/dist/generators/cli.js.map +1 -0
  80. package/dist/generators/dsl-writer/index.d.ts +33 -0
  81. package/dist/generators/dsl-writer/index.d.ts.map +1 -0
  82. package/dist/generators/dsl-writer/index.js +226 -0
  83. package/dist/generators/dsl-writer/index.js.map +1 -0
  84. package/dist/generators/gherkin-parser/index.d.ts +47 -0
  85. package/dist/generators/gherkin-parser/index.d.ts.map +1 -0
  86. package/dist/generators/gherkin-parser/index.js +149 -0
  87. package/dist/generators/gherkin-parser/index.js.map +1 -0
  88. package/dist/generators/gherkin-parser/selector-extractor.d.ts +37 -0
  89. package/dist/generators/gherkin-parser/selector-extractor.d.ts.map +1 -0
  90. package/dist/generators/gherkin-parser/selector-extractor.js +108 -0
  91. package/dist/generators/gherkin-parser/selector-extractor.js.map +1 -0
  92. package/dist/generators/scaffold-generator/index.d.ts +111 -0
  93. package/dist/generators/scaffold-generator/index.d.ts.map +1 -0
  94. package/dist/generators/scaffold-generator/index.js +408 -0
  95. package/dist/generators/scaffold-generator/index.js.map +1 -0
  96. package/dist/generators/selector-mapper/ai-mapper.d.ts +56 -0
  97. package/dist/generators/selector-mapper/ai-mapper.d.ts.map +1 -0
  98. package/dist/generators/selector-mapper/ai-mapper.js +457 -0
  99. package/dist/generators/selector-mapper/ai-mapper.js.map +1 -0
  100. package/dist/generators/selector-mapper/hybrid-mapper.d.ts +67 -0
  101. package/dist/generators/selector-mapper/hybrid-mapper.d.ts.map +1 -0
  102. package/dist/generators/selector-mapper/hybrid-mapper.js +349 -0
  103. package/dist/generators/selector-mapper/hybrid-mapper.js.map +1 -0
  104. package/dist/generators/selector-mapper/index.d.ts +8 -0
  105. package/dist/generators/selector-mapper/index.d.ts.map +1 -0
  106. package/dist/generators/selector-mapper/index.js +12 -0
  107. package/dist/generators/selector-mapper/index.js.map +1 -0
  108. package/dist/generators/selector-mapper/intelligent-mapper.d.ts +125 -0
  109. package/dist/generators/selector-mapper/intelligent-mapper.d.ts.map +1 -0
  110. package/dist/generators/selector-mapper/intelligent-mapper.js +391 -0
  111. package/dist/generators/selector-mapper/intelligent-mapper.js.map +1 -0
  112. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +49 -0
  113. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -0
  114. package/dist/generators/test-generator/adapters/adapter-interface.js +7 -0
  115. package/dist/generators/test-generator/adapters/adapter-interface.js.map +1 -0
  116. package/dist/generators/test-generator/adapters/adapter-registry.d.ts +29 -0
  117. package/dist/generators/test-generator/adapters/adapter-registry.d.ts.map +1 -0
  118. package/dist/generators/test-generator/adapters/adapter-registry.js +50 -0
  119. package/dist/generators/test-generator/adapters/adapter-registry.js.map +1 -0
  120. package/dist/generators/test-generator/adapters/index.d.ts +4 -0
  121. package/dist/generators/test-generator/adapters/index.d.ts.map +1 -0
  122. package/dist/generators/test-generator/adapters/index.js +13 -0
  123. package/dist/generators/test-generator/adapters/index.js.map +1 -0
  124. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +23 -0
  125. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -0
  126. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js +38 -0
  127. package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -0
  128. package/dist/generators/test-generator/adapters/playwright/templates/before-each.hbs +8 -0
  129. package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +5 -0
  130. package/dist/generators/test-generator/adapters/playwright/templates/scenario.hbs +8 -0
  131. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +1 -0
  132. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/clear-action.hbs +1 -0
  133. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-action.hbs +1 -0
  134. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/double-click-action.hbs +1 -0
  135. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -0
  136. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/hover-action.hbs +1 -0
  137. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/press-action.hbs +1 -0
  138. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -0
  139. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +1 -0
  140. package/dist/generators/test-generator/adapters/playwright/templates/steps/active-state-assertion.hbs +2 -0
  141. package/dist/generators/test-generator/adapters/playwright/templates/steps/ai-response-assertion-selector.hbs +5 -0
  142. package/dist/generators/test-generator/adapters/playwright/templates/steps/ai-response-assertion-simple.hbs +1 -0
  143. package/dist/generators/test-generator/adapters/playwright/templates/steps/application-running.hbs +1 -0
  144. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +1 -0
  145. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -0
  146. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +1 -0
  147. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -0
  148. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -0
  149. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -0
  150. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +1 -0
  151. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -0
  152. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -0
  153. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +1 -0
  154. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +1 -0
  155. package/dist/generators/test-generator/adapters/playwright/templates/steps/check-action.hbs +1 -0
  156. package/dist/generators/test-generator/adapters/playwright/templates/steps/checkbox.hbs +2 -0
  157. package/dist/generators/test-generator/adapters/playwright/templates/steps/checked-assertion.hbs +1 -0
  158. package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-action.hbs +1 -0
  159. package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-auth.hbs +6 -0
  160. package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-browser-state.hbs +6 -0
  161. package/dist/generators/test-generator/adapters/playwright/templates/steps/clear-database.hbs +4 -0
  162. package/dist/generators/test-generator/adapters/playwright/templates/steps/clear.hbs +2 -0
  163. package/dist/generators/test-generator/adapters/playwright/templates/steps/click-action.hbs +1 -0
  164. package/dist/generators/test-generator/adapters/playwright/templates/steps/click.hbs +2 -0
  165. package/dist/generators/test-generator/adapters/playwright/templates/steps/contain-text-assertion.hbs +1 -0
  166. package/dist/generators/test-generator/adapters/playwright/templates/steps/contains-text-assertion.hbs +2 -0
  167. package/dist/generators/test-generator/adapters/playwright/templates/steps/count-assertion.hbs +1 -0
  168. package/dist/generators/test-generator/adapters/playwright/templates/steps/count-greater-than.hbs +3 -0
  169. package/dist/generators/test-generator/adapters/playwright/templates/steps/count-less-than.hbs +3 -0
  170. package/dist/generators/test-generator/adapters/playwright/templates/steps/disabled-assertion.hbs +2 -0
  171. package/dist/generators/test-generator/adapters/playwright/templates/steps/displayed-containing-text.hbs +3 -0
  172. package/dist/generators/test-generator/adapters/playwright/templates/steps/displayed-with-text.hbs +3 -0
  173. package/dist/generators/test-generator/adapters/playwright/templates/steps/double-click-action.hbs +1 -0
  174. package/dist/generators/test-generator/adapters/playwright/templates/steps/empty-assertion-advanced.hbs +3 -0
  175. package/dist/generators/test-generator/adapters/playwright/templates/steps/empty-assertion.hbs +2 -0
  176. package/dist/generators/test-generator/adapters/playwright/templates/steps/enabled-assertion.hbs +2 -0
  177. package/dist/generators/test-generator/adapters/playwright/templates/steps/error-message-assertion.hbs +3 -0
  178. package/dist/generators/test-generator/adapters/playwright/templates/steps/fill-action.hbs +1 -0
  179. package/dist/generators/test-generator/adapters/playwright/templates/steps/fill.hbs +2 -0
  180. package/dist/generators/test-generator/adapters/playwright/templates/steps/focused-assertion.hbs +1 -0
  181. package/dist/generators/test-generator/adapters/playwright/templates/steps/generic-message-assertion.hbs +3 -0
  182. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-attribute.hbs +2 -0
  183. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-class.hbs +2 -0
  184. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-count.hbs +2 -0
  185. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-image-src.hbs +3 -0
  186. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-link.hbs +3 -0
  187. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-placeholder.hbs +3 -0
  188. package/dist/generators/test-generator/adapters/playwright/templates/steps/has-value.hbs +2 -0
  189. package/dist/generators/test-generator/adapters/playwright/templates/steps/have-text-assertion.hbs +1 -0
  190. package/dist/generators/test-generator/adapters/playwright/templates/steps/hover-action.hbs +1 -0
  191. package/dist/generators/test-generator/adapters/playwright/templates/steps/html5-validation-check.hbs +4 -0
  192. package/dist/generators/test-generator/adapters/playwright/templates/steps/is-checked.hbs +2 -0
  193. package/dist/generators/test-generator/adapters/playwright/templates/steps/is-editable.hbs +2 -0
  194. package/dist/generators/test-generator/adapters/playwright/templates/steps/is-focused.hbs +2 -0
  195. package/dist/generators/test-generator/adapters/playwright/templates/steps/is-hidden.hbs +2 -0
  196. package/dist/generators/test-generator/adapters/playwright/templates/steps/is-unchecked.hbs +2 -0
  197. package/dist/generators/test-generator/adapters/playwright/templates/steps/locator.hbs +1 -0
  198. package/dist/generators/test-generator/adapters/playwright/templates/steps/login.hbs +8 -0
  199. package/dist/generators/test-generator/adapters/playwright/templates/steps/message-assertion-body.hbs +1 -0
  200. package/dist/generators/test-generator/adapters/playwright/templates/steps/message-assertion-selector.hbs +2 -0
  201. package/dist/generators/test-generator/adapters/playwright/templates/steps/message-count-assertion.hbs +3 -0
  202. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -0
  203. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/route-assertion.hbs +2 -0
  204. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element.hbs +1 -0
  205. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-timeout.hbs +1 -0
  206. package/dist/generators/test-generator/adapters/playwright/templates/steps/navigation.hbs +1 -0
  207. package/dist/generators/test-generator/adapters/playwright/templates/steps/not-checked-assertion.hbs +1 -0
  208. package/dist/generators/test-generator/adapters/playwright/templates/steps/not-visible-assertion.hbs +1 -0
  209. package/dist/generators/test-generator/adapters/playwright/templates/steps/not-visible.hbs +2 -0
  210. package/dist/generators/test-generator/adapters/playwright/templates/steps/notification-assertion.hbs +4 -0
  211. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +1 -0
  212. package/dist/generators/test-generator/adapters/playwright/templates/steps/press-action.hbs +1 -0
  213. package/dist/generators/test-generator/adapters/playwright/templates/steps/press-enter.hbs +2 -0
  214. package/dist/generators/test-generator/adapters/playwright/templates/steps/redirect-assertion.hbs +3 -0
  215. package/dist/generators/test-generator/adapters/playwright/templates/steps/route-assertion.hbs +2 -0
  216. package/dist/generators/test-generator/adapters/playwright/templates/steps/screen-navigation.hbs +3 -0
  217. package/dist/generators/test-generator/adapters/playwright/templates/steps/scroll-bottom-assertion.hbs +5 -0
  218. package/dist/generators/test-generator/adapters/playwright/templates/steps/select-action.hbs +1 -0
  219. package/dist/generators/test-generator/adapters/playwright/templates/steps/select.hbs +2 -0
  220. package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/application-running.hbs +1 -0
  221. package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-auth.hbs +6 -0
  222. package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-browser-state.hbs +6 -0
  223. package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/clear-database.hbs +4 -0
  224. package/dist/generators/test-generator/adapters/playwright/templates/steps/setup/user-login-todo.hbs +6 -0
  225. package/dist/generators/test-generator/adapters/playwright/templates/steps/text-matches-pattern.hbs +3 -0
  226. package/dist/generators/test-generator/adapters/playwright/templates/steps/uncheck-action.hbs +1 -0
  227. package/dist/generators/test-generator/adapters/playwright/templates/steps/user-login-todo.hbs +6 -0
  228. package/dist/generators/test-generator/adapters/playwright/templates/steps/visibility-assertion.hbs +2 -0
  229. package/dist/generators/test-generator/adapters/playwright/templates/steps/visible-assertion.hbs +1 -0
  230. package/dist/generators/test-generator/adapters/playwright/templates/steps/wait-for-element.hbs +1 -0
  231. package/dist/generators/test-generator/adapters/playwright/templates/steps/wait-timeout.hbs +1 -0
  232. package/dist/generators/test-generator/adapters/playwright/templates/steps/wait.hbs +1 -0
  233. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +19 -0
  234. package/dist/generators/test-generator/ai-step-mapper.d.ts +27 -0
  235. package/dist/generators/test-generator/ai-step-mapper.d.ts.map +1 -0
  236. package/dist/generators/test-generator/ai-step-mapper.js +204 -0
  237. package/dist/generators/test-generator/ai-step-mapper.js.map +1 -0
  238. package/dist/generators/test-generator/code-generator.d.ts +52 -0
  239. package/dist/generators/test-generator/code-generator.d.ts.map +1 -0
  240. package/dist/generators/test-generator/code-generator.js +191 -0
  241. package/dist/generators/test-generator/code-generator.js.map +1 -0
  242. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts +7 -0
  243. package/dist/generators/test-generator/patterns/assertion-patterns.d.ts.map +1 -0
  244. package/dist/generators/test-generator/patterns/assertion-patterns.js +173 -0
  245. package/dist/generators/test-generator/patterns/assertion-patterns.js.map +1 -0
  246. package/dist/generators/test-generator/patterns/form-patterns.d.ts +8 -0
  247. package/dist/generators/test-generator/patterns/form-patterns.d.ts.map +1 -0
  248. package/dist/generators/test-generator/patterns/form-patterns.js +110 -0
  249. package/dist/generators/test-generator/patterns/form-patterns.js.map +1 -0
  250. package/dist/generators/test-generator/patterns/index.d.ts +45 -0
  251. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -0
  252. package/dist/generators/test-generator/patterns/index.js +106 -0
  253. package/dist/generators/test-generator/patterns/index.js.map +1 -0
  254. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts +7 -0
  255. package/dist/generators/test-generator/patterns/interaction-patterns.d.ts.map +1 -0
  256. package/dist/generators/test-generator/patterns/interaction-patterns.js +100 -0
  257. package/dist/generators/test-generator/patterns/interaction-patterns.js.map +1 -0
  258. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts +8 -0
  259. package/dist/generators/test-generator/patterns/navigation-patterns.d.ts.map +1 -0
  260. package/dist/generators/test-generator/patterns/navigation-patterns.js +92 -0
  261. package/dist/generators/test-generator/patterns/navigation-patterns.js.map +1 -0
  262. package/dist/generators/test-generator/patterns/setup-patterns.d.ts +7 -0
  263. package/dist/generators/test-generator/patterns/setup-patterns.d.ts.map +1 -0
  264. package/dist/generators/test-generator/patterns/setup-patterns.js +84 -0
  265. package/dist/generators/test-generator/patterns/setup-patterns.js.map +1 -0
  266. package/dist/generators/test-generator/patterns/types.d.ts +38 -0
  267. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -0
  268. package/dist/generators/test-generator/patterns/types.js +3 -0
  269. package/dist/generators/test-generator/patterns/types.js.map +1 -0
  270. package/dist/generators/test-generator/step-mapper-old.d.ts +180 -0
  271. package/dist/generators/test-generator/step-mapper-old.d.ts.map +1 -0
  272. package/dist/generators/test-generator/step-mapper-old.js +752 -0
  273. package/dist/generators/test-generator/step-mapper-old.js.map +1 -0
  274. package/dist/generators/test-generator/step-mapper-refactored.d.ts +47 -0
  275. package/dist/generators/test-generator/step-mapper-refactored.d.ts.map +1 -0
  276. package/dist/generators/test-generator/step-mapper-refactored.js +182 -0
  277. package/dist/generators/test-generator/step-mapper-refactored.js.map +1 -0
  278. package/dist/generators/test-generator/step-mapper.d.ts +66 -0
  279. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -0
  280. package/dist/generators/test-generator/step-mapper.js +248 -0
  281. package/dist/generators/test-generator/step-mapper.js.map +1 -0
  282. package/dist/generators/test-generator/template-engine.d.ts +33 -0
  283. package/dist/generators/test-generator/template-engine.d.ts.map +1 -0
  284. package/dist/generators/test-generator/template-engine.js +129 -0
  285. package/dist/generators/test-generator/template-engine.js.map +1 -0
  286. package/dist/generators/test-generator/utils/data-resolver.d.ts +39 -0
  287. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -0
  288. package/dist/generators/test-generator/utils/data-resolver.js +162 -0
  289. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -0
  290. package/dist/generators/test-generator/utils/path-inference.d.ts +49 -0
  291. package/dist/generators/test-generator/utils/path-inference.d.ts.map +1 -0
  292. package/dist/generators/test-generator/utils/path-inference.js +286 -0
  293. package/dist/generators/test-generator/utils/path-inference.js.map +1 -0
  294. package/dist/generators/test-generator/utils/selector-resolver.d.ts +93 -0
  295. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -0
  296. package/dist/generators/test-generator/utils/selector-resolver.js +408 -0
  297. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -0
  298. package/dist/generators/types.d.ts +118 -0
  299. package/dist/generators/types.d.ts.map +1 -0
  300. package/dist/generators/types.js +48 -0
  301. package/dist/generators/types.js.map +1 -0
  302. package/dist/generators/ui-model-builder/deep-scanner.d.ts +121 -0
  303. package/dist/generators/ui-model-builder/deep-scanner.d.ts.map +1 -0
  304. package/dist/generators/ui-model-builder/deep-scanner.js +1113 -0
  305. package/dist/generators/ui-model-builder/deep-scanner.js.map +1 -0
  306. package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts +110 -0
  307. package/dist/generators/ui-model-builder/enhanced-deep-scanner.d.ts.map +1 -0
  308. package/dist/generators/ui-model-builder/enhanced-deep-scanner.js +608 -0
  309. package/dist/generators/ui-model-builder/enhanced-deep-scanner.js.map +1 -0
  310. package/dist/generators/ui-model-builder/react-scanner.d.ts +107 -0
  311. package/dist/generators/ui-model-builder/react-scanner.d.ts.map +1 -0
  312. package/dist/generators/ui-model-builder/react-scanner.js +797 -0
  313. package/dist/generators/ui-model-builder/react-scanner.js.map +1 -0
  314. package/dist/input/cli-adapter.d.ts +63 -0
  315. package/dist/input/cli-adapter.d.ts.map +1 -0
  316. package/dist/input/cli-adapter.js +173 -0
  317. package/dist/input/cli-adapter.js.map +1 -0
  318. package/dist/input/config-adapter.d.ts +25 -0
  319. package/dist/input/config-adapter.d.ts.map +1 -0
  320. package/dist/input/config-adapter.js +70 -0
  321. package/dist/input/config-adapter.js.map +1 -0
  322. package/dist/input/input-adapter.d.ts +28 -0
  323. package/dist/input/input-adapter.d.ts.map +1 -0
  324. package/dist/input/input-adapter.js +17 -0
  325. package/dist/input/input-adapter.js.map +1 -0
  326. package/dist/input/vscode-adapter.d.ts +62 -0
  327. package/dist/input/vscode-adapter.d.ts.map +1 -0
  328. package/dist/input/vscode-adapter.js +64 -0
  329. package/dist/input/vscode-adapter.js.map +1 -0
  330. package/dist/orchestrator/cache-manager.d.ts +37 -0
  331. package/dist/orchestrator/cache-manager.d.ts.map +1 -0
  332. package/dist/orchestrator/cache-manager.js +148 -0
  333. package/dist/orchestrator/cache-manager.js.map +1 -0
  334. package/dist/orchestrator/pipeline.d.ts +73 -0
  335. package/dist/orchestrator/pipeline.d.ts.map +1 -0
  336. package/dist/orchestrator/pipeline.js +607 -0
  337. package/dist/orchestrator/pipeline.js.map +1 -0
  338. package/dist/orchestrator/project-initializer.d.ts +51 -0
  339. package/dist/orchestrator/project-initializer.d.ts.map +1 -0
  340. package/dist/orchestrator/project-initializer.js +326 -0
  341. package/dist/orchestrator/project-initializer.js.map +1 -0
  342. package/dist/orchestrator/reporter.d.ts +15 -0
  343. package/dist/orchestrator/reporter.d.ts.map +1 -0
  344. package/dist/orchestrator/reporter.js +30 -0
  345. package/dist/orchestrator/reporter.js.map +1 -0
  346. package/dist/orchestrator/screen-manager.d.ts +47 -0
  347. package/dist/orchestrator/screen-manager.d.ts.map +1 -0
  348. package/dist/orchestrator/screen-manager.js +271 -0
  349. package/dist/orchestrator/screen-manager.js.map +1 -0
  350. package/dist/tools/auto-tagger.d.ts +107 -0
  351. package/dist/tools/auto-tagger.d.ts.map +1 -0
  352. package/dist/tools/auto-tagger.js +502 -0
  353. package/dist/tools/auto-tagger.js.map +1 -0
  354. package/package.json +73 -0
  355. package/src/cli/commands/auto-tag-command.ts +80 -0
  356. package/src/cli/index.ts +205 -0
  357. package/src/config/ai-providers.yaml +56 -0
  358. package/src/config/config-loader.ts +248 -0
  359. package/src/config/config-schema.ts +148 -0
  360. package/src/config/default.config.yaml +101 -0
  361. package/src/config/framework.config.yaml +52 -0
  362. package/src/config/routes.yaml +31 -0
  363. package/src/core/selector-base/annotation-handler.ts +127 -0
  364. package/src/core/selector-base/base-generator.ts +234 -0
  365. package/src/core/selector-base/gherkin-parser.ts +57 -0
  366. package/src/core/selector-mapper/priority-mapper.ts +607 -0
  367. package/src/core/ui-scanner/heuristics/base-heuristic.ts +216 -0
  368. package/src/core/ui-scanner/react-scanner.ts +156 -0
  369. package/src/core/ui-scanner/scanner-interface.ts +133 -0
  370. package/src/core/ui-scanner/strict-scanner.ts +629 -0
  371. package/src/executor/playwright/playwright-generator.ts +125 -0
  372. package/src/executor/test-generator.ts +90 -0
  373. package/src/external/ai-provider.ts +90 -0
  374. package/src/external/anthropic-provider.ts +114 -0
  375. package/src/generators/README.md +410 -0
  376. package/src/generators/cache/cache-manager.ts +322 -0
  377. package/src/generators/cli.ts +640 -0
  378. package/src/generators/dsl-writer/index.ts +253 -0
  379. package/src/generators/gherkin-parser/index.ts +155 -0
  380. package/src/generators/gherkin-parser/selector-extractor.ts +142 -0
  381. package/src/generators/scaffold-generator/index.ts +524 -0
  382. package/src/generators/selector-mapper/ai-mapper.ts +528 -0
  383. package/src/generators/selector-mapper/hybrid-mapper.ts +427 -0
  384. package/src/generators/selector-mapper/index.ts +10 -0
  385. package/src/generators/selector-mapper/intelligent-mapper.ts +530 -0
  386. package/src/generators/test-generator/adapters/adapter-interface.ts +49 -0
  387. package/src/generators/test-generator/adapters/adapter-registry.ts +56 -0
  388. package/src/generators/test-generator/adapters/index.ts +9 -0
  389. package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +40 -0
  390. package/src/generators/test-generator/adapters/playwright/templates/before-each.hbs +8 -0
  391. package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +5 -0
  392. package/src/generators/test-generator/adapters/playwright/templates/scenario.hbs +8 -0
  393. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +1 -0
  394. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/clear-action.hbs +1 -0
  395. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-action.hbs +1 -0
  396. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/double-click-action.hbs +1 -0
  397. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -0
  398. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/hover-action.hbs +1 -0
  399. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/press-action.hbs +1 -0
  400. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -0
  401. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +1 -0
  402. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +1 -0
  403. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -0
  404. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/count-assertion.hbs +1 -0
  405. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-assertion.hbs +2 -0
  406. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/empty-assertion.hbs +2 -0
  407. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/enabled-assertion.hbs +2 -0
  408. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/focused-assertion.hbs +1 -0
  409. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -0
  410. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +1 -0
  411. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-visible-assertion.hbs +1 -0
  412. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-assertion.hbs +1 -0
  413. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/navigation.hbs +1 -0
  414. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/route-assertion.hbs +2 -0
  415. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-for-element.hbs +1 -0
  416. package/src/generators/test-generator/adapters/playwright/templates/steps/navigation/wait-timeout.hbs +1 -0
  417. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +1 -0
  418. package/src/generators/test-generator/adapters/playwright/templates/steps/setup/application-running.hbs +1 -0
  419. package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-auth.hbs +6 -0
  420. package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-browser-state.hbs +6 -0
  421. package/src/generators/test-generator/adapters/playwright/templates/steps/setup/clear-database.hbs +4 -0
  422. package/src/generators/test-generator/adapters/playwright/templates/steps/setup/user-login-todo.hbs +6 -0
  423. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +19 -0
  424. package/src/generators/test-generator/ai-step-mapper.ts +224 -0
  425. package/src/generators/test-generator/code-generator.ts +235 -0
  426. package/src/generators/test-generator/patterns/assertion-patterns.ts +183 -0
  427. package/src/generators/test-generator/patterns/form-patterns.ts +124 -0
  428. package/src/generators/test-generator/patterns/index.ts +97 -0
  429. package/src/generators/test-generator/patterns/interaction-patterns.ts +119 -0
  430. package/src/generators/test-generator/patterns/navigation-patterns.ts +110 -0
  431. package/src/generators/test-generator/patterns/setup-patterns.ts +94 -0
  432. package/src/generators/test-generator/patterns/types.ts +41 -0
  433. package/src/generators/test-generator/step-mapper.ts +254 -0
  434. package/src/generators/test-generator/template-engine.ts +160 -0
  435. package/src/generators/test-generator/utils/data-resolver.ts +147 -0
  436. package/src/generators/test-generator/utils/path-inference.ts +344 -0
  437. package/src/generators/test-generator/utils/selector-resolver.ts +480 -0
  438. package/src/generators/types.ts +226 -0
  439. package/src/generators/ui-model-builder/deep-scanner.ts +1244 -0
  440. package/src/generators/ui-model-builder/enhanced-deep-scanner.ts +731 -0
  441. package/src/generators/ui-model-builder/react-scanner.ts +959 -0
  442. package/src/input/cli-adapter.ts +185 -0
  443. package/src/input/config-adapter.ts +71 -0
  444. package/src/input/input-adapter.ts +32 -0
  445. package/src/input/vscode-adapter.ts +90 -0
  446. package/src/orchestrator/cache-manager.ts +138 -0
  447. package/src/orchestrator/pipeline.ts +713 -0
  448. package/src/orchestrator/project-initializer.ts +315 -0
  449. package/src/orchestrator/reporter.ts +36 -0
  450. package/src/orchestrator/screen-manager.ts +268 -0
  451. package/src/tools/auto-tagger.ts +572 -0
@@ -0,0 +1,1113 @@
1
+ "use strict";
2
+ /**
3
+ * PRODUCTION-READY Deep Scanner
4
+ * Implements all 4 iterations:
5
+ * 1. Deep Scanning (4+ levels)
6
+ * 2. UI Library Handling
7
+ * 3. Props Merging
8
+ * 4. Smart Filtering
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ var __importDefault = (this && this.__importDefault) || function (mod) {
44
+ return (mod && mod.__esModule) ? mod : { "default": mod };
45
+ };
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.DeepScanner = void 0;
48
+ const parser = __importStar(require("@babel/parser"));
49
+ const traverse_1 = __importDefault(require("@babel/traverse"));
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const FRAMEWORK_COMPONENTS = {
53
+ // Next.js Framework Components
54
+ 'next/link': {
55
+ Link: {
56
+ convertTo: 'a',
57
+ role: 'link',
58
+ propsMap: {
59
+ href: 'href',
60
+ // Next.js Link wraps children in <a>, so we preserve all props
61
+ }
62
+ }
63
+ },
64
+ 'next/image': {
65
+ Image: {
66
+ convertTo: 'img',
67
+ role: 'img',
68
+ propsMap: {
69
+ src: 'src',
70
+ alt: 'alt',
71
+ width: 'width',
72
+ height: 'height',
73
+ loading: 'loading',
74
+ priority: (attrs) => attrs.priority ? 'eager' : 'lazy'
75
+ }
76
+ }
77
+ },
78
+ 'next/script': {
79
+ Script: {
80
+ convertTo: 'script',
81
+ propsMap: {
82
+ src: 'src',
83
+ strategy: 'strategy',
84
+ onLoad: 'onload'
85
+ }
86
+ }
87
+ },
88
+ // React Router Components (for future support)
89
+ 'react-router-dom': {
90
+ Link: {
91
+ convertTo: 'a',
92
+ role: 'link',
93
+ propsMap: {
94
+ to: 'href' // React Router uses 'to' instead of 'href'
95
+ }
96
+ },
97
+ NavLink: {
98
+ convertTo: 'a',
99
+ role: 'link',
100
+ propsMap: {
101
+ to: 'href'
102
+ }
103
+ }
104
+ },
105
+ // Remix Framework Components (for future support)
106
+ 'remix': {
107
+ Link: {
108
+ convertTo: 'a',
109
+ role: 'link',
110
+ propsMap: {
111
+ to: 'href'
112
+ }
113
+ },
114
+ Form: {
115
+ convertTo: 'form',
116
+ role: 'form',
117
+ propsMap: {
118
+ action: 'action',
119
+ method: 'method'
120
+ }
121
+ }
122
+ }
123
+ };
124
+ // UI libraries to scan (don't skip these)
125
+ const UI_LIBRARIES = [
126
+ 'flowbite-react',
127
+ '@mui/material',
128
+ '@mui/icons-material',
129
+ 'antd',
130
+ 'react-bootstrap',
131
+ '@headlessui/react',
132
+ '@radix-ui',
133
+ 'chakra-ui',
134
+ '@chakra-ui/react',
135
+ 'semantic-ui-react',
136
+ '@fluentui/react'
137
+ ];
138
+ // Skip these patterns (REFINED - don't skip framework UI components)
139
+ const SKIP_PATTERNS = [
140
+ /^react$/,
141
+ /^react-dom$/,
142
+ /^next$/, // Skip 'next' core package
143
+ /^next\/navigation$/, // Skip navigation hooks (not UI)
144
+ /^next\/headers$/, // Skip headers (not UI)
145
+ /^next\/server$/, // Skip server utilities (not UI)
146
+ /^next\/font/, // Skip font utilities (not UI)
147
+ // NOTE: We DON'T skip 'next/link', 'next/image', 'next/script' anymore!
148
+ /Icon$/,
149
+ /^Logo$/,
150
+ /^Copyright$/,
151
+ /^RenderIf$/,
152
+ /^Fragment$/
153
+ ];
154
+ // Prioritize scanning these patterns
155
+ const PRIORITY_PATTERNS = [
156
+ /Input$/,
157
+ /Field$/,
158
+ /Select$/,
159
+ /Checkbox$/,
160
+ /Radio$/,
161
+ /Button$/,
162
+ /TextArea$/,
163
+ /Form$/,
164
+ /TextField$/,
165
+ /Switch$/,
166
+ /Slider$/
167
+ ];
168
+ // ============================================================================
169
+ // Deep Scanner Class
170
+ // ============================================================================
171
+ class DeepScanner {
172
+ constructor(config) {
173
+ this.elements = [];
174
+ this.elementCounter = 0;
175
+ this.scannedFiles = new Set(); // Track file+props combinations
176
+ this.componentCache = new Map(); // Cache parsed components
177
+ this.config = config;
178
+ }
179
+ /**
180
+ * Main entry point - scan files deeply
181
+ */
182
+ async scanFiles(filePaths) {
183
+ this.elements = [];
184
+ this.elementCounter = 0;
185
+ this.scannedFiles.clear();
186
+ for (const filePath of filePaths) {
187
+ await this.scanFile(filePath, 0, {});
188
+ }
189
+ return this.elements;
190
+ }
191
+ /**
192
+ * Check if component should be scanned
193
+ */
194
+ shouldScanComponent(componentName, importPath) {
195
+ // Skip patterns
196
+ if (SKIP_PATTERNS.some(pattern => pattern.test(componentName))) {
197
+ return false;
198
+ }
199
+ if (SKIP_PATTERNS.some(pattern => pattern.test(importPath))) {
200
+ return false;
201
+ }
202
+ // Always scan priority components
203
+ if (PRIORITY_PATTERNS.some(pattern => pattern.test(componentName))) {
204
+ return true;
205
+ }
206
+ // Scan UI library components
207
+ if (UI_LIBRARIES.some(lib => importPath.includes(lib))) {
208
+ return true;
209
+ }
210
+ // Scan custom components (starts with uppercase)
211
+ if (/^[A-Z]/.test(componentName)) {
212
+ return true;
213
+ }
214
+ return false;
215
+ }
216
+ /**
217
+ * Check if this is an actual DOM element OR a UI library component
218
+ * We treat UI library components as terminal elements (don't scan deeper)
219
+ */
220
+ isActualDOMElement(tagName) {
221
+ const domElements = [
222
+ 'input', 'button', 'textarea', 'select', 'form',
223
+ 'a', 'img', 'video', 'audio', 'canvas',
224
+ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
225
+ 'p', 'span', 'div', 'section', 'article',
226
+ 'table', 'tr', 'td', 'th', 'thead', 'tbody',
227
+ 'ul', 'ol', 'li', 'dl', 'dt', 'dd',
228
+ 'label', 'fieldset', 'legend'
229
+ ];
230
+ return domElements.includes(tagName.toLowerCase());
231
+ }
232
+ /**
233
+ * Check if this is a UI library component (flowbite, MUI, etc.)
234
+ * These are treated as "actual elements" for our purposes
235
+ */
236
+ isUILibraryComponent(tagName, importPath) {
237
+ // Check if from UI library
238
+ const isFromUILibrary = UI_LIBRARIES.some(lib => importPath.includes(lib));
239
+ if (!isFromUILibrary)
240
+ return false;
241
+ // For UI libraries, check if it's a form/interactive component
242
+ // (not layout/utility components like Flowbite, ThemeProvider, etc.)
243
+ const uiComponentPatterns = [
244
+ /Input/i, // TextInput, InputText, Input, EmailInput
245
+ /TextField/i,
246
+ /Button/i,
247
+ /Select/i,
248
+ /Checkbox/i,
249
+ /Radio/i,
250
+ /Switch/i,
251
+ /Slider/i,
252
+ /TextArea/i,
253
+ /Dropdown/i,
254
+ /Modal/i,
255
+ /Dialog/i,
256
+ /Picker/i, // DatePicker, TimePicker
257
+ /Toggle/i
258
+ ];
259
+ const matchesPattern = uiComponentPatterns.some(pattern => pattern.test(tagName));
260
+ // Skip utility components
261
+ const utilityComponents = ['Flowbite', 'ThemeProvider', 'Label', 'HelperText'];
262
+ const isUtility = utilityComponents.some(util => tagName.includes(util));
263
+ return matchesPattern && !isUtility;
264
+ }
265
+ /**
266
+ * Check if component is a custom wrapper of UI library component
267
+ * E.g., Button wraps ButtonFlowbite, TextInput wraps InputText
268
+ * These should be treated as terminal elements (buttons/inputs)
269
+ */
270
+ isCustomUIWrapper(componentName, importPath) {
271
+ // Not from UI library (it's custom)
272
+ const isFromUILib = UI_LIBRARIES.some(lib => importPath.includes(lib));
273
+ if (isFromUILib)
274
+ return false;
275
+ // Must be from project's components
276
+ if (!importPath.includes('@/components'))
277
+ return false;
278
+ // Match patterns that suggest it's a UI wrapper
279
+ const wrapperPatterns = [
280
+ /Button$/i, // Button, SubmitButton, etc.
281
+ /Input$/i, // TextInput, EmailInput, etc.
282
+ /Select$/i,
283
+ /Checkbox$/i,
284
+ /Radio$/i,
285
+ /TextArea$/i,
286
+ /Switch$/i,
287
+ /Slider$/i,
288
+ /Link$/i // Custom Link components
289
+ ];
290
+ return wrapperPatterns.some(pattern => pattern.test(componentName));
291
+ }
292
+ /**
293
+ * Check if component is a framework component (Next.js, React Router, etc.)
294
+ * Returns component definition if found, null otherwise
295
+ */
296
+ getFrameworkComponent(componentName, importPath) {
297
+ // Check if import path is in FRAMEWORK_COMPONENTS registry
298
+ if (!FRAMEWORK_COMPONENTS[importPath]) {
299
+ return null;
300
+ }
301
+ // Check if component name exists for this import path
302
+ const componentDef = FRAMEWORK_COMPONENTS[importPath][componentName];
303
+ return componentDef || null;
304
+ }
305
+ /**
306
+ * Map framework component props to target DOM element props
307
+ */
308
+ mapFrameworkProps(attrs, propsMap) {
309
+ const mapped = {};
310
+ // Map each prop according to propsMap
311
+ for (const [sourceKey, targetKey] of Object.entries(propsMap)) {
312
+ if (attrs[sourceKey] !== undefined) {
313
+ if (typeof targetKey === 'function') {
314
+ // Custom mapping function
315
+ mapped[sourceKey] = targetKey(attrs);
316
+ }
317
+ else {
318
+ // Direct prop name mapping
319
+ mapped[targetKey] = attrs[sourceKey];
320
+ }
321
+ }
322
+ }
323
+ // Preserve unmapped props that might be useful
324
+ const mappedKeys = new Set(Object.keys(propsMap));
325
+ for (const [key, value] of Object.entries(attrs)) {
326
+ if (!mappedKeys.has(key) && !mapped[key]) {
327
+ // Keep useful attributes like className, id, data-testid, etc.
328
+ if (['className', 'id', 'data-testid', 'aria-label', 'aria-labelledby', 'role'].includes(key)) {
329
+ mapped[key] = value;
330
+ }
331
+ }
332
+ }
333
+ return mapped;
334
+ }
335
+ /**
336
+ * Convert framework component to synthetic DOM element
337
+ */
338
+ convertFrameworkComponent(componentName, componentDef, attrs, text, sourceFileName) {
339
+ // Map props according to component definition
340
+ const mappedProps = this.mapFrameworkProps(attrs, componentDef.propsMap);
341
+ // Apply default props if any
342
+ const finalProps = {
343
+ ...componentDef.defaultProps,
344
+ ...mappedProps
345
+ };
346
+ // Create synthetic element
347
+ const element = {
348
+ key: `e${++this.elementCounter}`,
349
+ tag: componentDef.convertTo,
350
+ role: componentDef.role || this.inferRole(componentDef.convertTo),
351
+ id: finalProps.id,
352
+ name: finalProps.name,
353
+ placeholder: finalProps.placeholder,
354
+ ariaLabel: finalProps['aria-label'] || finalProps.ariaLabel,
355
+ testId: finalProps['data-testid'],
356
+ text: text,
357
+ source: sourceFileName,
358
+ props: finalProps
359
+ };
360
+ // Add metadata to track this was converted from framework component
361
+ element.metadata = {
362
+ frameworkComponent: componentName,
363
+ originalImport: 'framework' // Will be set by caller
364
+ };
365
+ return element;
366
+ }
367
+ /**
368
+ * Try to resolve a path with different file extensions
369
+ */
370
+ tryResolveWithExtensions(basePath) {
371
+ const extensions = ['.tsx', '.ts', '.jsx', '.js'];
372
+ // Try direct file with extensions
373
+ for (const ext of extensions) {
374
+ const fullPath = basePath + ext;
375
+ if (fs.existsSync(fullPath)) {
376
+ return fullPath;
377
+ }
378
+ }
379
+ // Try index files
380
+ for (const ext of extensions) {
381
+ const indexPath = path.join(basePath, `index${ext}`);
382
+ if (fs.existsSync(indexPath)) {
383
+ return indexPath;
384
+ }
385
+ }
386
+ return null;
387
+ }
388
+ /**
389
+ * Resolve import path to actual file path
390
+ * Enhanced to try multiple locations for @/ alias
391
+ */
392
+ resolveComponentPath(importPath, sourceFile) {
393
+ try {
394
+ // Handle @ alias - Try multiple locations (Next.js, CRA, custom)
395
+ if (importPath.startsWith('@/')) {
396
+ const relativePath = importPath.replace('@/', '');
397
+ // Try multiple candidate directories in priority order
398
+ const candidates = [
399
+ path.join(this.config.projectRoot, 'src', relativePath), // CRA, Vite: src/
400
+ path.join(this.config.projectRoot, 'app', relativePath), // Next.js 13+: app/
401
+ path.join(this.config.projectRoot, relativePath), // Root level
402
+ path.join(this.config.sourceRoot, '..', relativePath), // Relative to sourceRoot
403
+ ];
404
+ for (const candidate of candidates) {
405
+ const resolved = this.tryResolveWithExtensions(candidate);
406
+ if (resolved) {
407
+ if (this.config.verbose) {
408
+ console.log(` ✓ Resolved @/${relativePath} → ${path.relative(this.config.projectRoot, resolved)}`);
409
+ }
410
+ return resolved;
411
+ }
412
+ }
413
+ // If not found, log for debugging
414
+ if (this.config.verbose) {
415
+ console.log(` ⚠️ Could not resolve @/${relativePath} (tried: src/, app/, root/)`);
416
+ }
417
+ return null;
418
+ }
419
+ // Handle relative imports
420
+ else if (importPath.startsWith('./') || importPath.startsWith('../')) {
421
+ const sourceDir = path.dirname(sourceFile);
422
+ const resolvedPath = path.resolve(sourceDir, importPath);
423
+ return this.tryResolveWithExtensions(resolvedPath);
424
+ }
425
+ // Handle UI library imports (node_modules)
426
+ else if (UI_LIBRARIES.some(lib => importPath.includes(lib))) {
427
+ const nodeModulesPath = path.join(this.config.projectRoot, 'node_modules', importPath);
428
+ // Try with extensions first
429
+ const resolved = this.tryResolveWithExtensions(nodeModulesPath);
430
+ if (resolved)
431
+ return resolved;
432
+ // Try package.json main/module field
433
+ const pkgPath = path.join(nodeModulesPath, 'package.json');
434
+ if (fs.existsSync(pkgPath)) {
435
+ try {
436
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
437
+ const mainFile = pkg.module || pkg.main || 'index.js';
438
+ const mainPath = path.join(nodeModulesPath, mainFile);
439
+ if (fs.existsSync(mainPath)) {
440
+ return mainPath;
441
+ }
442
+ }
443
+ catch (e) {
444
+ // Ignore package.json parse errors
445
+ }
446
+ }
447
+ return null;
448
+ }
449
+ // Skip other node_modules
450
+ else {
451
+ return null;
452
+ }
453
+ }
454
+ catch (error) {
455
+ return null;
456
+ }
457
+ }
458
+ /**
459
+ * Extract JSX element from conditional expression (ignoring the condition)
460
+ * This allows us to discover elements in modals, conditional blocks, etc.
461
+ */
462
+ extractConditionalJSXElement(jsxElement, sourceFileName, imports, componentProps, depth, detectedComponents) {
463
+ const openingElement = jsxElement.openingElement;
464
+ let tagName = '';
465
+ if (openingElement.name.type === 'JSXIdentifier') {
466
+ tagName = openingElement.name.name;
467
+ }
468
+ if (this.config.verbose && depth === 2) {
469
+ console.log(` ${' '.repeat(depth)} 🔄 Conditional JSX: <${tagName}>`);
470
+ }
471
+ // Extract attributes and merge with component props
472
+ const attrs = this.extractAttributes(openingElement.attributes, componentProps);
473
+ const text = this.extractTextContent(jsxElement);
474
+ const mergedAttrs = this.mergeProps(componentProps, attrs);
475
+ // Check if custom component (Modal, Button, Input, etc.)
476
+ if (/^[A-Z]/.test(tagName) && imports.has(tagName)) {
477
+ const importPath = imports.get(tagName);
478
+ // Check if it's a project component that should be deep scanned
479
+ if (importPath.startsWith('@/') || importPath.startsWith('./') || importPath.startsWith('../')) {
480
+ if (this.config.verbose) {
481
+ console.log(` ${' '.repeat(depth)} 🔄 Conditional component: <${tagName}> - adding to scan queue`);
482
+ }
483
+ // Add to detectedComponents queue for deep scanning
484
+ // This ensures <Input>, <Button>, etc. get fully scanned
485
+ detectedComponents.push({
486
+ name: tagName,
487
+ importPath,
488
+ props: mergedAttrs, // Use merged props for prop resolution
489
+ isProjectComponent: true
490
+ });
491
+ // Also recursively scan immediate JSX children (for containers like <Modal>)
492
+ // This will discover HTML elements and additional custom components
493
+ if (jsxElement.children && jsxElement.children.length > 0) {
494
+ for (const child of jsxElement.children) {
495
+ if (child.type === 'JSXElement') {
496
+ this.extractConditionalJSXElement(child, sourceFileName, imports, componentProps, depth, detectedComponents);
497
+ }
498
+ else if (child.type === 'JSXExpressionContainer') {
499
+ const expr = child.expression;
500
+ if (expr.type === 'LogicalExpression' && expr.operator === '&&' && expr.right.type === 'JSXElement') {
501
+ this.extractConditionalJSXElement(expr.right, sourceFileName, imports, componentProps, depth, detectedComponents);
502
+ }
503
+ else if (expr.type === 'ConditionalExpression') {
504
+ if (expr.consequent.type === 'JSXElement') {
505
+ this.extractConditionalJSXElement(expr.consequent, sourceFileName, imports, componentProps, depth, detectedComponents);
506
+ }
507
+ if (expr.alternate && expr.alternate.type === 'JSXElement') {
508
+ this.extractConditionalJSXElement(expr.alternate, sourceFileName, imports, componentProps, depth, detectedComponents);
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ }
515
+ return; // Don't add component itself as element
516
+ }
517
+ // HTML element - add directly
518
+ if (this.shouldIncludeElement(tagName, mergedAttrs, text)) {
519
+ this.elements.push({
520
+ key: `e${++this.elementCounter}`,
521
+ tag: tagName,
522
+ role: this.inferRole(tagName),
523
+ id: mergedAttrs.id,
524
+ name: mergedAttrs.name,
525
+ placeholder: mergedAttrs.placeholder,
526
+ ariaLabel: mergedAttrs['aria-label'] || mergedAttrs.ariaLabel,
527
+ testId: mergedAttrs['data-testid'],
528
+ text: text,
529
+ source: sourceFileName,
530
+ props: mergedAttrs
531
+ });
532
+ if (this.config.verbose) {
533
+ console.log(` ${' '.repeat(depth)} ✅ Found conditional element: <${tagName}> ${mergedAttrs['aria-label'] || mergedAttrs.ariaLabel ? `aria-label="${mergedAttrs['aria-label'] || mergedAttrs.ariaLabel}"` : ''}${text ? `text="${text.substring(0, 30)}"` : ''}`);
534
+ }
535
+ }
536
+ // Recursively process children
537
+ if (jsxElement.children && jsxElement.children.length > 0) {
538
+ for (const child of jsxElement.children) {
539
+ if (child.type === 'JSXElement') {
540
+ this.extractConditionalJSXElement(child, sourceFileName, imports, componentProps, depth, detectedComponents);
541
+ }
542
+ else if (child.type === 'JSXExpressionContainer') {
543
+ // Handle nested conditionals
544
+ const expr = child.expression;
545
+ if (expr.type === 'LogicalExpression' && expr.operator === '&&' && expr.right.type === 'JSXElement') {
546
+ this.extractConditionalJSXElement(expr.right, sourceFileName, imports, componentProps, depth, detectedComponents);
547
+ }
548
+ else if (expr.type === 'ConditionalExpression') {
549
+ if (expr.consequent.type === 'JSXElement') {
550
+ this.extractConditionalJSXElement(expr.consequent, sourceFileName, imports, componentProps, depth, detectedComponents);
551
+ }
552
+ if (expr.alternate && expr.alternate.type === 'JSXElement') {
553
+ this.extractConditionalJSXElement(expr.alternate, sourceFileName, imports, componentProps, depth, detectedComponents);
554
+ }
555
+ }
556
+ }
557
+ }
558
+ }
559
+ }
560
+ /**
561
+ * Extract attributes from JSX element
562
+ * Now supports resolving expressions from componentProps!
563
+ */
564
+ extractAttributes(attributes, componentProps = {}) {
565
+ const attrs = {};
566
+ attributes.forEach(attr => {
567
+ if (attr.type !== 'JSXAttribute' || attr.name.type !== 'JSXIdentifier') {
568
+ return;
569
+ }
570
+ const attrName = attr.name.name;
571
+ let value = null;
572
+ if (!attr.value) {
573
+ value = true; // Boolean attribute like <input required />
574
+ }
575
+ else if (attr.value.type === 'StringLiteral') {
576
+ value = attr.value.value;
577
+ }
578
+ else if (attr.value.type === 'JSXExpressionContainer') {
579
+ const expr = attr.value.expression;
580
+ if (expr.type === 'BooleanLiteral') {
581
+ value = expr.value;
582
+ }
583
+ else if (expr.type === 'StringLiteral') {
584
+ value = expr.value;
585
+ }
586
+ else if (expr.type === 'NumericLiteral') {
587
+ value = expr.value;
588
+ }
589
+ else if (expr.type === 'MemberExpression') {
590
+ // props.name → try to resolve from componentProps
591
+ if (expr.object.type === 'Identifier' && expr.object.name === 'props' &&
592
+ expr.property.type === 'Identifier') {
593
+ const propName = expr.property.name;
594
+ // Try to resolve from componentProps
595
+ if (componentProps[propName] !== undefined) {
596
+ value = componentProps[propName]; // ✅ RESOLVED!
597
+ }
598
+ else {
599
+ // Keep as expression if can't resolve
600
+ value = `{props.${propName}}`;
601
+ }
602
+ }
603
+ }
604
+ else if (expr.type === 'Identifier') {
605
+ // Handle: className={inputClassName}
606
+ // Try to resolve from componentProps
607
+ const identifierName = expr.name;
608
+ if (componentProps[identifierName] !== undefined) {
609
+ value = componentProps[identifierName]; // ✅ RESOLVED!
610
+ }
611
+ else {
612
+ // Keep as expression
613
+ value = `{${identifierName}}`;
614
+ }
615
+ }
616
+ else if (expr.type === 'CallExpression') {
617
+ // Handle: register('email', ...) → extract first string argument
618
+ if (expr.arguments && expr.arguments.length > 0) {
619
+ const firstArg = expr.arguments[0];
620
+ if (firstArg.type === 'StringLiteral') {
621
+ // register('email') → use 'email' as value
622
+ value = firstArg.value; // ✅ EXTRACTED!
623
+ }
624
+ }
625
+ }
626
+ }
627
+ if (value !== null) {
628
+ attrs[attrName] = value;
629
+ }
630
+ });
631
+ return attrs;
632
+ }
633
+ /**
634
+ * Extract text content from JSX element
635
+ */
636
+ extractTextContent(node) {
637
+ let text = '';
638
+ if (!node.children)
639
+ return null;
640
+ node.children.forEach((child) => {
641
+ if (child.type === 'JSXText') {
642
+ const trimmed = child.value.trim();
643
+ if (trimmed) {
644
+ text += trimmed + ' ';
645
+ }
646
+ }
647
+ else if (child.type === 'JSXExpressionContainer') {
648
+ if (child.expression.type === 'StringLiteral') {
649
+ text += child.expression.value + ' ';
650
+ }
651
+ }
652
+ });
653
+ return text.trim() || null;
654
+ }
655
+ /**
656
+ * Check if element should be included in UI model
657
+ */
658
+ shouldIncludeElement(tagName, attrs, text) {
659
+ const lowercaseTag = tagName.toLowerCase();
660
+ // Priority 1: UI library components (ALWAYS include)
661
+ if (lowercaseTag.includes('input') || lowercaseTag.includes('button') || lowercaseTag.includes('select')) {
662
+ return true;
663
+ }
664
+ // Priority 2: Form elements (ALWAYS include)
665
+ const formElements = ['input', 'button', 'textarea', 'select', 'form'];
666
+ if (formElements.includes(lowercaseTag))
667
+ return true;
668
+ // Priority 2: Links
669
+ if (lowercaseTag === 'a' || tagName === 'Link')
670
+ return true;
671
+ // Priority 3: Elements with identifiers
672
+ if (attrs.id || attrs.name || attrs['data-testid'] || attrs['aria-label']) {
673
+ return true;
674
+ }
675
+ // Priority 4: Headings with text
676
+ if (/^h[1-6]$/.test(lowercaseTag) && text)
677
+ return true;
678
+ // Priority 5: Images with alt
679
+ if (lowercaseTag === 'img' && attrs.alt)
680
+ return true;
681
+ // Priority 6: Interactive elements
682
+ if (attrs.onClick || attrs.onSubmit || attrs.href)
683
+ return true;
684
+ // Skip generic containers unless meaningful
685
+ if (['div', 'span', 'p'].includes(lowercaseTag)) {
686
+ return !!(text && text.length > 5); // Only if meaningful text
687
+ }
688
+ // Include semantic elements
689
+ if (['section', 'article', 'nav', 'header', 'footer', 'main'].includes(lowercaseTag)) {
690
+ return true;
691
+ }
692
+ return false;
693
+ }
694
+ /**
695
+ * Merge props from component chain
696
+ */
697
+ mergeProps(componentProps, elementAttrs) {
698
+ const merged = { ...componentProps };
699
+ // Element attributes override component props
700
+ Object.entries(elementAttrs).forEach(([key, value]) => {
701
+ // Handle prop expressions like {props.name}
702
+ if (typeof value === 'string' && value.startsWith('{props.')) {
703
+ const propName = value.match(/\{props\.(\w+)\}/)?.[1];
704
+ if (propName && componentProps[propName]) {
705
+ merged[key] = componentProps[propName];
706
+ }
707
+ else {
708
+ merged[key] = value; // Keep expression as-is
709
+ }
710
+ }
711
+ else {
712
+ merged[key] = value;
713
+ }
714
+ });
715
+ return merged;
716
+ }
717
+ /**
718
+ * Generate cache key for file+props combination
719
+ * This allows same component to be scanned multiple times with different props
720
+ */
721
+ getCacheKey(filePath, componentProps) {
722
+ // Create a stable key from significant props (id, name, ariaLabel, etc.)
723
+ const significantProps = ['id', 'name', 'ariaLabel', 'aria-label', 'placeholder', 'type', 'testId', 'data-testid'];
724
+ const propsSignature = significantProps
725
+ .map(key => componentProps[key] || componentProps[key.replace(/-/g, '')])
726
+ .filter(Boolean)
727
+ .join('|');
728
+ return `${filePath}::${propsSignature}`;
729
+ }
730
+ /**
731
+ * Recursively scan a file for UI elements
732
+ */
733
+ async scanFile(filePath, depth, componentProps) {
734
+ // Safety checks
735
+ if (depth >= this.config.maxDepth) {
736
+ if (this.config.verbose) {
737
+ console.log(` ${' '.repeat(depth)}⚠️ Max depth ${this.config.maxDepth} reached`);
738
+ }
739
+ return;
740
+ }
741
+ // Use cache key based on file+props to allow same component with different props
742
+ const cacheKey = this.getCacheKey(filePath, componentProps);
743
+ if (this.scannedFiles.has(cacheKey)) {
744
+ if (this.config.verbose) {
745
+ console.log(` ${' '.repeat(depth)}⚠️ Already scanned: ${path.basename(filePath)} with these props`);
746
+ }
747
+ return;
748
+ }
749
+ if (!fs.existsSync(filePath)) {
750
+ if (this.config.verbose) {
751
+ console.log(` ${' '.repeat(depth)}⚠️ File not found: ${filePath}`);
752
+ }
753
+ return;
754
+ }
755
+ this.scannedFiles.add(cacheKey);
756
+ if (this.config.verbose) {
757
+ console.log(` ${' '.repeat(depth)}📄 Scanning (L${depth}): ${path.basename(filePath)}`);
758
+ }
759
+ // Read and parse file
760
+ const code = fs.readFileSync(filePath, 'utf-8');
761
+ let ast;
762
+ try {
763
+ ast = parser.parse(code, {
764
+ sourceType: 'module',
765
+ plugins: ['typescript', 'jsx']
766
+ });
767
+ }
768
+ catch (error) {
769
+ if (this.config.verbose) {
770
+ console.log(` ${' '.repeat(depth)}⚠️ Parse error: ${error.message}`);
771
+ }
772
+ return;
773
+ }
774
+ // Extract imports and JSX elements
775
+ const self = this; // Capture 'this' and avoid naming conflict
776
+ const sourceFileName = path.basename(filePath);
777
+ const imports = new Map();
778
+ const detectedComponents = [];
779
+ let jsxElementCount = 0;
780
+ (0, traverse_1.default)(ast, {
781
+ ImportDeclaration: (path) => {
782
+ const importPath = path.node.source.value;
783
+ // Default import
784
+ const defaultSpec = path.node.specifiers.find((s) => s.type === 'ImportDefaultSpecifier');
785
+ if (defaultSpec) {
786
+ imports.set(defaultSpec.local.name, importPath);
787
+ }
788
+ // Named imports
789
+ path.node.specifiers
790
+ .filter((s) => s.type === 'ImportSpecifier')
791
+ .forEach((spec) => {
792
+ if (spec.imported.type === 'Identifier') {
793
+ // Use LOCAL name (after 'as'), not imported name
794
+ imports.set(spec.local.name, importPath);
795
+ if (self.config.verbose && depth === 2) {
796
+ console.log(` ${' '.repeat(depth)} Import: ${spec.imported.name} as ${spec.local.name} from ${importPath}`);
797
+ }
798
+ }
799
+ });
800
+ }
801
+ });
802
+ // Extract JSX elements
803
+ (0, traverse_1.default)(ast, {
804
+ JSXElement: (nodePath) => {
805
+ jsxElementCount++;
806
+ const openingElement = nodePath.node.openingElement;
807
+ let tagName = '';
808
+ if (openingElement.name.type === 'JSXIdentifier') {
809
+ tagName = openingElement.name.name;
810
+ }
811
+ if (self.config.verbose && depth === 2) {
812
+ console.log(` ${' '.repeat(depth)} JSX #${jsxElementCount}: <${tagName}>`);
813
+ }
814
+ // Extract attributes WITH componentProps for expression resolution
815
+ const attrs = self.extractAttributes(openingElement.attributes, componentProps);
816
+ const text = self.extractTextContent(nodePath.node);
817
+ // Merge with component props (for additional props not in attrs)
818
+ const mergedAttrs = self.mergeProps(componentProps, attrs);
819
+ // Check if custom component
820
+ if (/^[A-Z]/.test(tagName) && imports.has(tagName)) {
821
+ const importPath = imports.get(tagName);
822
+ if (self.config.verbose) {
823
+ console.log(` ${' '.repeat(depth)} 🔎 Component: <${tagName}> from ${importPath}`);
824
+ }
825
+ // NEW: Check if framework component (Next.js Link, Image, etc.)
826
+ const frameworkComp = self.getFrameworkComponent(tagName, importPath);
827
+ if (frameworkComp) {
828
+ // Convert to synthetic DOM element
829
+ const syntheticElement = self.convertFrameworkComponent(tagName, frameworkComp, mergedAttrs, text, sourceFileName);
830
+ // Update metadata with actual import path
831
+ syntheticElement.metadata.originalImport = importPath;
832
+ // Add to elements
833
+ self.elements.push(syntheticElement);
834
+ if (self.config.verbose) {
835
+ console.log(` ${' '.repeat(depth)} ✅ Found Framework Component: <${tagName}> → <${frameworkComp.convertTo}> ${mergedAttrs.href ? `href="${mergedAttrs.href}"` : ''}${text ? `text="${text.substring(0, 30)}"` : ''}`);
836
+ }
837
+ // Don't scan deeper - framework components are terminal
838
+ return;
839
+ }
840
+ // Check if UI library component - treat as terminal element
841
+ const isUILib = self.isUILibraryComponent(tagName, importPath);
842
+ if (self.config.verbose && depth === 2) {
843
+ console.log(` ${' '.repeat(depth)} isUILibraryComponent(${tagName}, ${importPath}) = ${isUILib}`);
844
+ }
845
+ if (isUILib) {
846
+ // Treat as actual element (don't scan deeper)
847
+ if (self.shouldIncludeElement(tagName, mergedAttrs, text)) {
848
+ self.elements.push({
849
+ key: `e${++self.elementCounter}`,
850
+ tag: tagName.toLowerCase(), // e.g. "inputtext"
851
+ role: self.inferRole('input'), // Infer as input
852
+ id: mergedAttrs.id,
853
+ name: mergedAttrs.name,
854
+ placeholder: mergedAttrs.placeholder,
855
+ ariaLabel: mergedAttrs['aria-label'] || mergedAttrs.ariaLabel,
856
+ testId: mergedAttrs['data-testid'],
857
+ text: text,
858
+ source: sourceFileName,
859
+ props: mergedAttrs
860
+ });
861
+ if (self.config.verbose) {
862
+ console.log(` ${' '.repeat(depth)} ✅ Found UI Library: <${tagName}> ${mergedAttrs.id ? `id="${mergedAttrs.id}"` : ''}${mergedAttrs['data-testid'] ? `data-testid="${mergedAttrs['data-testid']}"` : ''}`);
863
+ }
864
+ }
865
+ }
866
+ // NEW: Project components - ALWAYS try to scan deeper
867
+ else if (importPath.startsWith('@/') || importPath.startsWith('./') || importPath.startsWith('../')) {
868
+ // This is a project component - add to scan queue
869
+ // We'll scan it after traverse completes (cannot await in traverse callback)
870
+ if (self.config.verbose) {
871
+ console.log(` ${' '.repeat(depth)} → Will scan project component: <${tagName}>`);
872
+ }
873
+ detectedComponents.push({
874
+ name: tagName,
875
+ importPath,
876
+ props: mergedAttrs, // Use merged props (includes component props)
877
+ isProjectComponent: true // Mark as project component
878
+ });
879
+ }
880
+ // For other components (3rd party libs not in UI_LIBRARIES), decide whether to scan
881
+ else if (self.shouldScanComponent(tagName, importPath)) {
882
+ detectedComponents.push({
883
+ name: tagName,
884
+ importPath,
885
+ props: attrs,
886
+ isProjectComponent: false
887
+ });
888
+ }
889
+ }
890
+ // Check if actual DOM element
891
+ else if (self.isActualDOMElement(tagName)) {
892
+ // Found actual element - include it!
893
+ if (self.shouldIncludeElement(tagName, mergedAttrs, text)) {
894
+ self.elements.push({
895
+ key: `e${++self.elementCounter}`,
896
+ tag: tagName.toLowerCase(),
897
+ role: self.inferRole(tagName),
898
+ id: mergedAttrs.id,
899
+ name: mergedAttrs.name,
900
+ placeholder: mergedAttrs.placeholder,
901
+ ariaLabel: mergedAttrs['aria-label'] || mergedAttrs.ariaLabel,
902
+ testId: mergedAttrs['data-testid'],
903
+ text: text,
904
+ source: sourceFileName,
905
+ props: mergedAttrs
906
+ });
907
+ if (self.config.verbose) {
908
+ console.log(` ${' '.repeat(depth)} ✅ Found: <${tagName}> ${mergedAttrs.id ? `id="${mergedAttrs.id}"` : ''}${mergedAttrs['data-testid'] ? `data-testid="${mergedAttrs['data-testid']}"` : ''}`);
909
+ }
910
+ }
911
+ }
912
+ },
913
+ // NEW: Handle conditional rendering - ignore conditions, scan all JSX
914
+ JSXExpressionContainer: (nodePath) => {
915
+ const expr = nodePath.node.expression;
916
+ // Pattern 1: Logical AND (condition && <Element />)
917
+ if (expr.type === 'LogicalExpression' && expr.operator === '&&') {
918
+ const rightSide = expr.right;
919
+ if (rightSide.type === 'JSXElement') {
920
+ // Extract element from condition, ignoring the condition itself
921
+ self.extractConditionalJSXElement(rightSide, sourceFileName, imports, componentProps, depth, detectedComponents);
922
+ }
923
+ else if (rightSide.type === 'JSXFragment') {
924
+ // Handle {condition && (<><Element1 /><Element2 /></>)}
925
+ for (const child of rightSide.children) {
926
+ if (child.type === 'JSXElement') {
927
+ self.extractConditionalJSXElement(child, sourceFileName, imports, componentProps, depth, detectedComponents);
928
+ }
929
+ }
930
+ }
931
+ }
932
+ // Pattern 2: Ternary operator (condition ? <A /> : <B />)
933
+ if (expr.type === 'ConditionalExpression') {
934
+ const consequent = expr.consequent;
935
+ const alternate = expr.alternate;
936
+ // Extract BOTH branches (we want all possible UI)
937
+ if (consequent.type === 'JSXElement') {
938
+ self.extractConditionalJSXElement(consequent, sourceFileName, imports, componentProps, depth, detectedComponents);
939
+ }
940
+ if (alternate && alternate.type === 'JSXElement') {
941
+ self.extractConditionalJSXElement(alternate, sourceFileName, imports, componentProps, depth, detectedComponents);
942
+ }
943
+ }
944
+ // Pattern 3: Array.map() rendering
945
+ if (expr.type === 'CallExpression') {
946
+ const callee = expr.callee;
947
+ if (callee.type === 'MemberExpression' &&
948
+ callee.property.type === 'Identifier' &&
949
+ callee.property.name === 'map') {
950
+ const mapCallback = expr.arguments[0];
951
+ if (mapCallback &&
952
+ (mapCallback.type === 'ArrowFunctionExpression' ||
953
+ mapCallback.type === 'FunctionExpression')) {
954
+ const body = mapCallback.body;
955
+ // Direct return: arr.map(item => <Element />)
956
+ if (body.type === 'JSXElement') {
957
+ self.extractConditionalJSXElement(body, sourceFileName, imports, componentProps, depth, detectedComponents);
958
+ }
959
+ // Block with return: arr.map(item => { return <Element /> })
960
+ if (body.type === 'BlockStatement') {
961
+ for (const statement of body.body) {
962
+ if (statement.type === 'ReturnStatement' &&
963
+ statement.argument &&
964
+ statement.argument.type === 'JSXElement') {
965
+ self.extractConditionalJSXElement(statement.argument, sourceFileName, imports, componentProps, depth, detectedComponents);
966
+ }
967
+ }
968
+ }
969
+ }
970
+ }
971
+ }
972
+ }
973
+ });
974
+ if (this.config.verbose && detectedComponents.length > 0) {
975
+ console.log(` ${' '.repeat(depth)} → Found ${detectedComponents.length} component(s) to scan deeper`);
976
+ }
977
+ // Recursively scan detected components
978
+ for (const comp of detectedComponents) {
979
+ const componentPath = this.resolveComponentPath(comp.importPath, filePath);
980
+ if (componentPath) {
981
+ const elementsBefore = this.elements.length;
982
+ if (this.config.verbose) {
983
+ console.log(` ${' '.repeat(depth)} → Diving into: ${comp.name}`);
984
+ }
985
+ await this.scanFile(componentPath, depth + 1, comp.props);
986
+ const elementsAfter = this.elements.length;
987
+ const elementsFound = elementsAfter - elementsBefore;
988
+ // Project components: Check if found elements, if not, try fallback
989
+ if (comp.isProjectComponent && elementsFound === 0) {
990
+ // No elements found inside project component - try fallback as wrapper
991
+ if (this.config.verbose) {
992
+ console.log(` ${' '.repeat(depth)} ⚠️ <${comp.name}> scanned but empty, trying wrapper fallback`);
993
+ }
994
+ if (this.isCustomUIWrapper(comp.name, comp.importPath)) {
995
+ // Extract as synthetic element
996
+ const { tag: elementType, role } = this.inferWrapperType(comp.name);
997
+ this.elements.push({
998
+ key: `e${++this.elementCounter}`,
999
+ tag: elementType,
1000
+ role: role,
1001
+ id: comp.props.id,
1002
+ name: comp.props.name,
1003
+ placeholder: comp.props.placeholder,
1004
+ ariaLabel: comp.props['aria-label'] || comp.props.ariaLabel,
1005
+ testId: comp.props['data-testid'],
1006
+ text: null,
1007
+ source: sourceFileName,
1008
+ props: comp.props
1009
+ });
1010
+ if (this.config.verbose) {
1011
+ console.log(` ${' '.repeat(depth)} ✅ Fallback: Extracted <${comp.name}> as ${elementType}`);
1012
+ }
1013
+ }
1014
+ }
1015
+ else if (this.config.verbose && elementsFound > 0) {
1016
+ console.log(` ${' '.repeat(depth)} ✅ Scanned <${comp.name}> → found ${elementsFound} element(s)`);
1017
+ }
1018
+ }
1019
+ else {
1020
+ // Cannot resolve path
1021
+ if (this.config.verbose) {
1022
+ console.log(` ${' '.repeat(depth)} ⚠️ Could not resolve: ${comp.name} from ${comp.importPath}`);
1023
+ }
1024
+ // For project components that cannot be resolved, try wrapper fallback
1025
+ if (comp.isProjectComponent && this.isCustomUIWrapper(comp.name, comp.importPath)) {
1026
+ const { tag: elementType, role } = this.inferWrapperType(comp.name);
1027
+ this.elements.push({
1028
+ key: `e${++this.elementCounter}`,
1029
+ tag: elementType,
1030
+ role: role,
1031
+ id: comp.props.id,
1032
+ name: comp.props.name,
1033
+ placeholder: comp.props.placeholder,
1034
+ ariaLabel: comp.props['aria-label'] || comp.props.ariaLabel,
1035
+ testId: comp.props['data-testid'],
1036
+ text: null,
1037
+ source: sourceFileName,
1038
+ props: comp.props
1039
+ });
1040
+ if (this.config.verbose) {
1041
+ console.log(` ${' '.repeat(depth)} ✅ Fallback (unresolved): Extracted <${comp.name}> as ${elementType}`);
1042
+ }
1043
+ }
1044
+ }
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Infer wrapper type from component name
1049
+ * Used for fallback when component is empty
1050
+ */
1051
+ inferWrapperType(componentName) {
1052
+ if (/Input$/i.test(componentName)) {
1053
+ return { tag: 'input', role: 'textbox' };
1054
+ }
1055
+ else if (/TextArea$/i.test(componentName)) {
1056
+ return { tag: 'textarea', role: 'textbox' };
1057
+ }
1058
+ else if (/Select$/i.test(componentName)) {
1059
+ return { tag: 'select', role: 'combobox' };
1060
+ }
1061
+ else if (/Link$/i.test(componentName)) {
1062
+ return { tag: 'a', role: 'link' };
1063
+ }
1064
+ else if (/Button$/i.test(componentName)) {
1065
+ return { tag: 'button', role: 'button' };
1066
+ }
1067
+ else {
1068
+ // Default to div
1069
+ return { tag: 'div', role: undefined };
1070
+ }
1071
+ }
1072
+ /**
1073
+ * Infer semantic role from tag name
1074
+ */
1075
+ inferRole(tagName) {
1076
+ const roleMap = {
1077
+ 'button': 'button',
1078
+ 'a': 'link',
1079
+ 'input': 'textbox',
1080
+ 'textarea': 'textbox',
1081
+ 'select': 'combobox',
1082
+ 'img': 'img',
1083
+ 'nav': 'navigation',
1084
+ 'main': 'main',
1085
+ 'header': 'banner',
1086
+ 'footer': 'contentinfo'
1087
+ };
1088
+ return roleMap[tagName.toLowerCase()];
1089
+ }
1090
+ /**
1091
+ * Get scan results with statistics
1092
+ */
1093
+ getResults() {
1094
+ const byTag = {};
1095
+ const formElements = [];
1096
+ this.elements.forEach(el => {
1097
+ byTag[el.tag] = (byTag[el.tag] || 0) + 1;
1098
+ const formTags = ['input', 'button', 'textarea', 'select', 'form'];
1099
+ if (formTags.includes(el.tag)) {
1100
+ formElements.push(el);
1101
+ }
1102
+ });
1103
+ return {
1104
+ totalElements: this.elements.length,
1105
+ filesScanned: this.scannedFiles.size,
1106
+ byTag,
1107
+ formElements,
1108
+ elements: this.elements
1109
+ };
1110
+ }
1111
+ }
1112
+ exports.DeepScanner = DeepScanner;
1113
+ //# sourceMappingURL=deep-scanner.js.map