@meistrari/tela-build 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 (295) hide show
  1. package/README.md +75 -0
  2. package/app.config.ts +73 -0
  3. package/components/tela/animated/animated-calculating-number.vue +16 -0
  4. package/components/tela/animated/animated-number.mdx +248 -0
  5. package/components/tela/animated/animated-number.stories.ts +52 -0
  6. package/components/tela/animated/animated-number.vue +23 -0
  7. package/components/tela/animated/animated-text.vue +124 -0
  8. package/components/tela/animated/animated-value.vue +68 -0
  9. package/components/tela/avatar/avatar.mdx +117 -0
  10. package/components/tela/avatar/avatar.stories.ts +62 -0
  11. package/components/tela/avatar/avatar.vue +71 -0
  12. package/components/tela/avatar/group/avatar-group.stories.ts +78 -0
  13. package/components/tela/avatar/group/avatar-group.vue +46 -0
  14. package/components/tela/badge/badge.mdx +154 -0
  15. package/components/tela/badge/badge.stories.ts +82 -0
  16. package/components/tela/badge/badge.vue +41 -0
  17. package/components/tela/button/button.mdx +155 -0
  18. package/components/tela/button/button.stories.ts +202 -0
  19. package/components/tela/button/button.vue +107 -0
  20. package/components/tela/card.vue +30 -0
  21. package/components/tela/chart/chart-bar.vue +58 -0
  22. package/components/tela/chat/chat.mdx +268 -0
  23. package/components/tela/chat/chat.stories.ts +253 -0
  24. package/components/tela/chat/command/index.vue +41 -0
  25. package/components/tela/chat/command/mention/index.vue +138 -0
  26. package/components/tela/chat/index.vue +112 -0
  27. package/components/tela/chat/pure-text-input/chat-text-input.vue +190 -0
  28. package/components/tela/chat/text-input/chat-text-input.stories.ts +128 -0
  29. package/components/tela/chat/text-input/index.vue +217 -0
  30. package/components/tela/chat/text-message/chat-text-message.stories.ts +138 -0
  31. package/components/tela/chat/text-message/index.vue +355 -0
  32. package/components/tela/chat/types.ts +19 -0
  33. package/components/tela/checkbox/checkbox-card.vue +30 -0
  34. package/components/tela/checkbox/checkbox.mdx +164 -0
  35. package/components/tela/checkbox/checkbox.stories.ts +104 -0
  36. package/components/tela/checkbox/checkbox.vue +43 -0
  37. package/components/tela/collapsible/Collapsible.vue +15 -0
  38. package/components/tela/collapsible/CollapsibleContent.vue +59 -0
  39. package/components/tela/collapsible/CollapsibleTrigger.vue +12 -0
  40. package/components/tela/collapsible/collapsible.mdx +157 -0
  41. package/components/tela/collapsible-section/collapsible-section.mdx +180 -0
  42. package/components/tela/collapsible-section/collapsible-section.stories.ts +53 -0
  43. package/components/tela/collapsible-section/collapsible-section.vue +51 -0
  44. package/components/tela/collapsible-section-with-actions.vue +98 -0
  45. package/components/tela/combobox/combobox-anchor.vue +24 -0
  46. package/components/tela/combobox/combobox-empty.vue +19 -0
  47. package/components/tela/combobox/combobox-group.vue +24 -0
  48. package/components/tela/combobox/combobox-indicator.vue +22 -0
  49. package/components/tela/combobox/combobox-input.vue +31 -0
  50. package/components/tela/combobox/combobox-item.vue +28 -0
  51. package/components/tela/combobox/combobox-label.vue +24 -0
  52. package/components/tela/combobox/combobox-list.vue +90 -0
  53. package/components/tela/combobox/combobox-module-selector.vue +366 -0
  54. package/components/tela/combobox/combobox-root.vue +15 -0
  55. package/components/tela/combobox/combobox-trigger.vue +12 -0
  56. package/components/tela/combobox/combobox.mdx +285 -0
  57. package/components/tela/combobox/combobox.stories.ts +232 -0
  58. package/components/tela/combobox/combobox.vue +497 -0
  59. package/components/tela/command/command-dialog.vue +22 -0
  60. package/components/tela/command/command-empty.vue +25 -0
  61. package/components/tela/command/command-group.vue +46 -0
  62. package/components/tela/command/command-input.vue +38 -0
  63. package/components/tela/command/command-item.vue +78 -0
  64. package/components/tela/command/command-list.vue +78 -0
  65. package/components/tela/command/command-separator.vue +23 -0
  66. package/components/tela/command/command-shortcut.vue +13 -0
  67. package/components/tela/command/command.vue +88 -0
  68. package/components/tela/command/dialog-base.vue +15 -0
  69. package/components/tela/command/dialog-content.vue +50 -0
  70. package/components/tela/command/utils.ts +15 -0
  71. package/components/tela/complex-table/complex-table-cell.stories.ts +145 -0
  72. package/components/tela/complex-table/complex-table-cell.vue +45 -0
  73. package/components/tela/complex-table/complex-table-header-cell.stories.ts +103 -0
  74. package/components/tela/complex-table/complex-table-header-cell.vue +48 -0
  75. package/components/tela/complex-table/complex-table-header.stories.ts +89 -0
  76. package/components/tela/complex-table/complex-table-header.vue +70 -0
  77. package/components/tela/complex-table/complex-table-row.vue +199 -0
  78. package/components/tela/complex-table/complex-table-virtualized.vue +326 -0
  79. package/components/tela/complex-table/complex-table.stories.ts +358 -0
  80. package/components/tela/complex-table/complex-table.vue +237 -0
  81. package/components/tela/complex-table/composables/table-common.ts +93 -0
  82. package/components/tela/complex-table/composables/table-selection.ts +87 -0
  83. package/components/tela/complex-table/composables/virtual-scroll.ts +252 -0
  84. package/components/tela/complex-table/styles/table-shared.css +170 -0
  85. package/components/tela/complex-table/types.ts +63 -0
  86. package/components/tela/complex-table/utils.ts +35 -0
  87. package/components/tela/confirm-button/confirm-button.vue +137 -0
  88. package/components/tela/confirmation-modal/confirmation-modal.vue +72 -0
  89. package/components/tela/copy-button.vue +86 -0
  90. package/components/tela/date-range-picker.vue +221 -0
  91. package/components/tela/dialog/dialog.mdx +170 -0
  92. package/components/tela/dialog/dialog.vue +182 -0
  93. package/components/tela/disabled-area.vue +16 -0
  94. package/components/tela/disclaimer/disclaimer.mdx +238 -0
  95. package/components/tela/disclaimer/disclaimer.stories.ts +196 -0
  96. package/components/tela/disclaimer/disclaimer.vue +125 -0
  97. package/components/tela/dropdown-menu/DropdownMenu.vue +121 -0
  98. package/components/tela/dropdown-menu/DropdownMenuCheckboxItem.vue +40 -0
  99. package/components/tela/dropdown-menu/DropdownMenuContent.vue +75 -0
  100. package/components/tela/dropdown-menu/DropdownMenuGroup.vue +12 -0
  101. package/components/tela/dropdown-menu/DropdownMenuItem.vue +137 -0
  102. package/components/tela/dropdown-menu/DropdownMenuLabel.vue +26 -0
  103. package/components/tela/dropdown-menu/DropdownMenuRadioGroup.vue +18 -0
  104. package/components/tela/dropdown-menu/DropdownMenuRadioItem.vue +40 -0
  105. package/components/tela/dropdown-menu/DropdownMenuRoot.vue +15 -0
  106. package/components/tela/dropdown-menu/DropdownMenuSeparator.vue +21 -0
  107. package/components/tela/dropdown-menu/DropdownMenuShortcut.vue +14 -0
  108. package/components/tela/dropdown-menu/DropdownMenuSub.vue +18 -0
  109. package/components/tela/dropdown-menu/DropdownMenuSubContent.vue +30 -0
  110. package/components/tela/dropdown-menu/DropdownMenuSubTrigger.vue +35 -0
  111. package/components/tela/dropdown-menu/DropdownMenuTrigger.vue +14 -0
  112. package/components/tela/dropdown-menu/dropdown-menu.mdx +265 -0
  113. package/components/tela/dropdown-menu/dropdown-menu.stories.ts +156 -0
  114. package/components/tela/expandable-input.vue +96 -0
  115. package/components/tela/file-drop.vue +37 -0
  116. package/components/tela/file-upload/file-upload.mdx +189 -0
  117. package/components/tela/file-upload/file-upload.stories.ts +48 -0
  118. package/components/tela/file-upload/file-upload.vue +205 -0
  119. package/components/tela/filters/checkbox-filter.stories.ts +218 -0
  120. package/components/tela/filters/checkbox-filter.vue +165 -0
  121. package/components/tela/filters/date-filter.stories.ts +258 -0
  122. package/components/tela/filters/date-filter.vue +200 -0
  123. package/components/tela/filters/user-filter.stories.ts +344 -0
  124. package/components/tela/filters/user-filter.vue +271 -0
  125. package/components/tela/hover-card/hover-card.mdx +221 -0
  126. package/components/tela/hover-card/hover-card.stories.ts +87 -0
  127. package/components/tela/hover-card/hover-card.vue +61 -0
  128. package/components/tela/icon/custom.vue +319 -0
  129. package/components/tela/icon/spinner.vue +12 -0
  130. package/components/tela/icon-button/icon-button.vue +114 -0
  131. package/components/tela/icon.vue +37 -0
  132. package/components/tela/initials.vue +28 -0
  133. package/components/tela/inline-input.vue +77 -0
  134. package/components/tela/input/input.mdx +182 -0
  135. package/components/tela/input/input.stories.ts +153 -0
  136. package/components/tela/input/tela-input.vue +240 -0
  137. package/components/tela/kbd/kbd-return.vue +6 -0
  138. package/components/tela/kbd/kbd.mdx +238 -0
  139. package/components/tela/kbd/kbd.vue +18 -0
  140. package/components/tela/label/label.mdx +121 -0
  141. package/components/tela/label/label.stories.ts +37 -0
  142. package/components/tela/label/label.vue +25 -0
  143. package/components/tela/link-decoration/link-decoration.vue +19 -0
  144. package/components/tela/live-label.vue +32 -0
  145. package/components/tela/long-press-button.vue +98 -0
  146. package/components/tela/menubar/menubar-content.vue +77 -0
  147. package/components/tela/menubar/menubar-item.vue +32 -0
  148. package/components/tela/menubar/menubar-label.vue +14 -0
  149. package/components/tela/menubar/menubar-menu.vue +12 -0
  150. package/components/tela/menubar/menubar-root.vue +30 -0
  151. package/components/tela/menubar/menubar-separator.vue +17 -0
  152. package/components/tela/menubar/menubar-shortcut.vue +14 -0
  153. package/components/tela/menubar/menubar-sub-content.vue +36 -0
  154. package/components/tela/menubar/menubar-sub-trigger.vue +28 -0
  155. package/components/tela/menubar/menubar-sub.vue +20 -0
  156. package/components/tela/menubar/menubar-trigger.vue +27 -0
  157. package/components/tela/menubar/menubar.vue +298 -0
  158. package/components/tela/modal/modal.mdx +145 -0
  159. package/components/tela/modal/modal.vue +242 -0
  160. package/components/tela/multiple-select/multiple-select.mdx +274 -0
  161. package/components/tela/multiple-select/multiple-select.stories.ts +325 -0
  162. package/components/tela/multiple-select/multiple-select.vue +666 -0
  163. package/components/tela/pane.vue +110 -0
  164. package/components/tela/popover/popover-content.vue +48 -0
  165. package/components/tela/popover/popover-trigger.vue +12 -0
  166. package/components/tela/popover/popover.mdx +239 -0
  167. package/components/tela/popover/popover.stories.ts +150 -0
  168. package/components/tela/popover/popover.vue +15 -0
  169. package/components/tela/popover-list/popover-list-nested.vue +104 -0
  170. package/components/tela/popover-list/popover-list.stories.ts +330 -0
  171. package/components/tela/popover-list/popover-list.vue +191 -0
  172. package/components/tela/radio-button.vue +66 -0
  173. package/components/tela/radio-group/radio-group-item.vue +40 -0
  174. package/components/tela/radio-group/radio-group-root.vue +26 -0
  175. package/components/tela/radio-group/radio-group.mdx +78 -0
  176. package/components/tela/radio-group/radio-group.stories.ts +106 -0
  177. package/components/tela/radio-group/radio-group.vue +23 -0
  178. package/components/tela/range-calendar.stories.ts +110 -0
  179. package/components/tela/range-calendar.vue +109 -0
  180. package/components/tela/scroll-area/scroll-area.mdx +183 -0
  181. package/components/tela/scroll-area/scroll-area.vue +30 -0
  182. package/components/tela/scroll-area/scroll-bar.vue +31 -0
  183. package/components/tela/segment-toggle.stories.ts +114 -0
  184. package/components/tela/segment-toggle.vue +66 -0
  185. package/components/tela/select-menu/select-menu-content.vue +106 -0
  186. package/components/tela/select-menu/select-menu-down-button.vue +20 -0
  187. package/components/tela/select-menu/select-menu-group.vue +16 -0
  188. package/components/tela/select-menu/select-menu-item.vue +40 -0
  189. package/components/tela/select-menu/select-menu-root.vue +15 -0
  190. package/components/tela/select-menu/select-menu-trigger.vue +34 -0
  191. package/components/tela/select-menu/select-menu-up-button.vue +20 -0
  192. package/components/tela/select-menu/select-menu-value.vue +12 -0
  193. package/components/tela/select-menu/select-menu.mdx +221 -0
  194. package/components/tela/select-menu/select-menu.stories.ts +91 -0
  195. package/components/tela/select-menu/select-menu.vue +165 -0
  196. package/components/tela/selector/selector.vue +47 -0
  197. package/components/tela/sheet/sheet-close.vue +12 -0
  198. package/components/tela/sheet/sheet-content.vue +57 -0
  199. package/components/tela/sheet/sheet-description.vue +23 -0
  200. package/components/tela/sheet/sheet-footer.vue +18 -0
  201. package/components/tela/sheet/sheet-header.vue +15 -0
  202. package/components/tela/sheet/sheet-root.vue +18 -0
  203. package/components/tela/sheet/sheet-title.vue +23 -0
  204. package/components/tela/sheet/sheet-trigger.vue +12 -0
  205. package/components/tela/sheet/sheet.client.vue +150 -0
  206. package/components/tela/sheet/sheet.mdx +176 -0
  207. package/components/tela/sheet/sheet.stories.ts +201 -0
  208. package/components/tela/sheet/variants.ts +22 -0
  209. package/components/tela/side-sheet/side-sheet.mdx +131 -0
  210. package/components/tela/side-sheet/side-sheet.stories.ts +134 -0
  211. package/components/tela/side-sheet/side-sheet.vue +106 -0
  212. package/components/tela/skeleton/skeleton.mdx +165 -0
  213. package/components/tela/skeleton/skeleton.stories.ts +35 -0
  214. package/components/tela/skeleton/skeleton.vue +45 -0
  215. package/components/tela/skeleton-icon.vue +24 -0
  216. package/components/tela/span.vue +24 -0
  217. package/components/tela/star-button.vue +70 -0
  218. package/components/tela/status/status-lean.vue +30 -0
  219. package/components/tela/status/status.mdx +187 -0
  220. package/components/tela/status/status.stories.ts +160 -0
  221. package/components/tela/status/status.vue +420 -0
  222. package/components/tela/status-bar/status-bar.mdx +178 -0
  223. package/components/tela/status-bar/status-bar.stories.ts +64 -0
  224. package/components/tela/status-bar/status-bar.vue +56 -0
  225. package/components/tela/status-bar/types.ts +5 -0
  226. package/components/tela/switch/switch.mdx +118 -0
  227. package/components/tela/switch/switch.stories.ts +80 -0
  228. package/components/tela/switch/switch.vue +56 -0
  229. package/components/tela/table/table-body.vue +13 -0
  230. package/components/tela/table/table-caption.vue +13 -0
  231. package/components/tela/table/table-cell.vue +20 -0
  232. package/components/tela/table/table-empty.vue +37 -0
  233. package/components/tela/table/table-footer.vue +13 -0
  234. package/components/tela/table/table-head.vue +13 -0
  235. package/components/tela/table/table-header.vue +13 -0
  236. package/components/tela/table/table-row.vue +13 -0
  237. package/components/tela/table/table.mdx +230 -0
  238. package/components/tela/table/table.stories.ts +384 -0
  239. package/components/tela/table/table.vue +15 -0
  240. package/components/tela/tabs/tabs-content.vue +20 -0
  241. package/components/tela/tabs/tabs-indicator.vue +22 -0
  242. package/components/tela/tabs/tabs-list.vue +23 -0
  243. package/components/tela/tabs/tabs-root.vue +15 -0
  244. package/components/tela/tabs/tabs-trigger.vue +27 -0
  245. package/components/tela/tabs/tabs.mdx +138 -0
  246. package/components/tela/tabs/tabs.stories.ts +72 -0
  247. package/components/tela/tabs/tabs.vue +61 -0
  248. package/components/tela/tags/tags-select.mdx +318 -0
  249. package/components/tela/tags/tags-select.stories.ts +47 -0
  250. package/components/tela/tags/tags-select.vue +637 -0
  251. package/components/tela/tags/tags.mdx +151 -0
  252. package/components/tela/tags/tags.stories.ts +118 -0
  253. package/components/tela/tags/tags.vue +112 -0
  254. package/components/tela/textarea/textarea.mdx +102 -0
  255. package/components/tela/textarea/textarea.stories.ts +50 -0
  256. package/components/tela/textarea/textarea.vue +34 -0
  257. package/components/tela/toggle-group.vue +91 -0
  258. package/components/tela/tooltip/tooltip-content.vue +45 -0
  259. package/components/tela/tooltip/tooltip-provider.vue +12 -0
  260. package/components/tela/tooltip/tooltip-root.vue +15 -0
  261. package/components/tela/tooltip/tooltip-trigger.vue +12 -0
  262. package/components/tela/tooltip/tooltip.mdx +196 -0
  263. package/components/tela/tooltip/tooltip.stories.ts +200 -0
  264. package/components/tela/tooltip/tooltip.vue +91 -0
  265. package/components/tela/tooltip-group/tooltip-group-trigger.vue +92 -0
  266. package/components/tela/tooltip-group/tooltip-group.mdx +236 -0
  267. package/components/tela/tooltip-group/tooltip-group.stories.ts +465 -0
  268. package/components/tela/tooltip-group/tooltip-group.vue +35 -0
  269. package/components/tela/transparent-input.vue +151 -0
  270. package/components/tela/variable-icon.vue +28 -0
  271. package/components/tela/variable-input.vue +77 -0
  272. package/components/tela/wide-button/wide-button.vue +40 -0
  273. package/components.json +18 -0
  274. package/composables/status-toast.ts +67 -0
  275. package/css/reset.css +386 -0
  276. package/css/text.css +22 -0
  277. package/lib/doc-generator.ts +903 -0
  278. package/lib/extractors/volar-extract.ts +186 -0
  279. package/lib/type-resolver.ts +402 -0
  280. package/lib/utils.ts +6 -0
  281. package/modules/tela-build-docs/index.ts +139 -0
  282. package/nuxt.config.ts +80 -0
  283. package/package.json +84 -0
  284. package/plugins/test-id.ts +7 -0
  285. package/tsconfig.json +7 -0
  286. package/types/custom-icon.ts +1 -0
  287. package/types/index.ts +2 -0
  288. package/types/status.ts +1 -0
  289. package/unocss.config.ts +89 -0
  290. package/utils/component-utils.ts +30 -0
  291. package/utils/design-tokens.ts +431 -0
  292. package/utils/fold.ts +8 -0
  293. package/utils/select-menu.ts +10 -0
  294. package/utils/status.ts +1 -0
  295. package/utils/without-keys.ts +34 -0
@@ -0,0 +1,186 @@
1
+ import { resolve, relative } from 'pathe'
2
+ import { existsSync, writeFileSync, mkdirSync } from 'node:fs'
3
+ import { createRequire } from 'node:module'
4
+ import type { createChecker } from 'vue-component-meta'
5
+
6
+ const require = createRequire(import.meta.url)
7
+
8
+ type MetaChecker = ReturnType<typeof createChecker>
9
+
10
+ let loadChecker: typeof import('vue-component-meta').createChecker | null = null
11
+
12
+ try {
13
+ loadChecker = require('vue-component-meta').createChecker
14
+ }
15
+ catch {
16
+ loadChecker = null
17
+ }
18
+
19
+ /**
20
+ * Creates (or reuses) a tiny tsconfig overlay so TypeScript/Volar can
21
+ * resolve imports from the cached layer location and the app root.
22
+ *
23
+ * We place it under <app>/.docs/tsconfig.tela-docs.json to ensure a writable path.
24
+ */
25
+ export function ensureOverlayTsconfig(appRootDir: string, layerRoot: string): string {
26
+ const docsDir = resolve(appRootDir, '.docs')
27
+ if (!existsSync(docsDir)) {
28
+ mkdirSync(docsDir, { recursive: true })
29
+ }
30
+ const overlayPath = resolve(docsDir, 'tsconfig.tela-docs.json')
31
+
32
+ // Try to extend the app's root tsconfig.json if present
33
+ const appTsconfig = resolve(appRootDir, 'tsconfig.json')
34
+ const extendsPath = existsSync(appTsconfig) ? './tsconfig.json' : undefined
35
+
36
+ // Paths must be relative to the overlay file
37
+ const relLayer = relative(docsDir, layerRoot).replace(/\\/g, '/')
38
+ const relApp = relative(docsDir, appRootDir).replace(/\\/g, '/')
39
+
40
+ const content = {
41
+ ...(extendsPath ? { extends: extendsPath } : {}),
42
+ compilerOptions: {
43
+ // Make the layer root the default base for bare imports (helps TS walk node_modules near layer)
44
+ baseUrl: relLayer,
45
+ // Common aliases that may be used by the build layer or apps
46
+ paths: {
47
+ '~/*': [`${relLayer}/*`],
48
+ '@/*': [`${relLayer}/*`],
49
+ },
50
+ // Keep type checking very light for speed; this file is only for metadata extraction
51
+ skipLibCheck: true,
52
+ },
53
+ include: [
54
+ `${relLayer}/**/*.ts`,
55
+ `${relLayer}/**/*.tsx`,
56
+ `${relLayer}/**/*.vue`,
57
+ // Let TS see the app's ambient types if there are any
58
+ `${relApp}/**/*.d.ts`,
59
+ ],
60
+ }
61
+
62
+ try {
63
+ writeFileSync(overlayPath, JSON.stringify(content, null, 2), 'utf-8')
64
+ }
65
+ catch {
66
+ }
67
+ return overlayPath
68
+ }
69
+
70
+ export interface VolarExtractor {
71
+ checker: MetaChecker
72
+ /** Extracts normalized component API info or throws on hard failure */
73
+ extract: (fileAbsPath: string) => {
74
+ name: string
75
+ description: string
76
+ props: Array<{
77
+ name: string
78
+ required: boolean
79
+ type: string
80
+ defaultValue?: { value: string }
81
+ description?: string
82
+ }>
83
+ events: Array<{
84
+ name: string
85
+ type?: string
86
+ description?: string
87
+ }>
88
+ slots: Array<{
89
+ name: string
90
+ // list of scoped bindings as `{ name, type }`
91
+ bindings?: Array<{ name: string; type?: string }>
92
+ description?: string
93
+ }>
94
+ }
95
+ }
96
+
97
+ /** Defensive stringify for type nodes coming from meta libs */
98
+ function displayType(t: any): string {
99
+ if (!t)
100
+ return 'any'
101
+ if (typeof t === 'string')
102
+ return t
103
+ // Try common shapes used by vue-component-meta
104
+ return (
105
+ t.display
106
+ ?? t.name
107
+ ?? (Array.isArray(t.names) ? t.names.join(' | ') : undefined)
108
+ ?? t.text
109
+ ?? 'any'
110
+ )
111
+ }
112
+
113
+ /** Normalize props from either array or record shapes */
114
+ function normalizeArrayOrRecord<T extends object>(val: any): T[] {
115
+ if (!val)
116
+ return []
117
+ if (Array.isArray(val))
118
+ return val as T[]
119
+ if (typeof val === 'object')
120
+ return Object.values(val)
121
+ return []
122
+ }
123
+
124
+ export function createVolarExtractor(appRootDir: string, layerRoot: string): VolarExtractor {
125
+ if (!loadChecker) {
126
+ throw new Error('vue-component-meta is not installed')
127
+ }
128
+
129
+ const createChecker = loadChecker
130
+
131
+ const tsconfigPath = ensureOverlayTsconfig(appRootDir, layerRoot)
132
+
133
+ const checker = createChecker(
134
+ tsconfigPath,
135
+ {
136
+ // Ensure the checker always uses the TypeScript program rather than a shim resolver
137
+ forceUseTs: true,
138
+ },
139
+ )
140
+
141
+ return {
142
+ checker,
143
+ extract(fileAbsPath: string) {
144
+ const meta: any = checker.getComponentMeta(fileAbsPath)
145
+
146
+ const name
147
+ = meta?.name
148
+ || meta?.displayName
149
+ // strip extension
150
+ || fileAbsPath.substring(fileAbsPath.lastIndexOf('/') + 1).replace(/\.vue$/, '')
151
+
152
+ const description: string
153
+ = meta?.docs?.description || meta?.description || ''
154
+
155
+ const props = normalizeArrayOrRecord<any>(meta?.props).map((p: any) => ({
156
+ name: p.name,
157
+ required: !!p.required,
158
+ type: displayType(p.type),
159
+ defaultValue: p.default != null
160
+ ? { value: String(p.default) }
161
+ : (p.defaultValue ? { value: String(p.defaultValue) } : undefined),
162
+ description: p.docs?.description || p.description,
163
+ }))
164
+
165
+ const events = normalizeArrayOrRecord<any>(meta?.emits).map((e: any) => ({
166
+ name: e.name,
167
+ type: displayType(e.type),
168
+ description: e.docs?.description || e.description,
169
+ }))
170
+
171
+ const slots = normalizeArrayOrRecord<any>(meta?.slots).map((s: any) => {
172
+ const bindings = normalizeArrayOrRecord<any>(s.props).map((b: any) => ({
173
+ name: b.name,
174
+ type: displayType(b.type),
175
+ }))
176
+ return {
177
+ name: s.name,
178
+ bindings,
179
+ description: s.docs?.description || s.description,
180
+ }
181
+ })
182
+
183
+ return { name, description, props, events, slots }
184
+ },
185
+ }
186
+ }
@@ -0,0 +1,402 @@
1
+ // Use type-only import so runtime doesn't require ts-morph unless available
2
+ import type { Project, SourceFile, TypeAliasDeclaration, InterfaceDeclaration, EnumDeclaration, Node } from 'ts-morph'
3
+ import { join } from 'pathe'
4
+ import { glob } from 'glob'
5
+ import { readFileSync } from 'node:fs'
6
+
7
+ const colors = {
8
+ gray: '\x1B[90m',
9
+ orange: '\x1B[38;5;208m',
10
+ purple: '\x1B[38;5;141m',
11
+ reset: '\x1B[0m',
12
+ }
13
+
14
+ export interface ResolvedType {
15
+ name: string
16
+ definition: string
17
+ raw: string
18
+ }
19
+
20
+ export class TypeResolver {
21
+ private project: Project | null = null
22
+ private typeRegistry = new Map<string, ResolvedType>()
23
+ private initialized = false
24
+
25
+ constructor(private projectRoot: string) {
26
+ // Defer creating ts-morph Project until initialize()
27
+ }
28
+
29
+ async initialize(): Promise<void> {
30
+ if (this.initialized)
31
+ return
32
+
33
+ // Remove verbose initialization message
34
+
35
+ // Attempt to load ts-morph dynamically. If not available, fall back gracefully.
36
+ try {
37
+ const mod: any = await import('ts-morph')
38
+ this.project = new mod.Project({
39
+ skipFileDependencyResolution: true,
40
+ skipLoadingLibFiles: true,
41
+ })
42
+ }
43
+ catch {
44
+ // ts-morph not installed; we'll operate with an empty registry
45
+ this.project = null
46
+ this.initialized = true
47
+ return
48
+ }
49
+
50
+ // Find all TypeScript files in the project
51
+ const tsFiles = await glob('**/*.ts', {
52
+ cwd: this.projectRoot,
53
+ ignore: ['node_modules/**', 'dist/**', '.nuxt/**', '.output/**'],
54
+ })
55
+
56
+ const vueFiles = await glob('**/*.vue', {
57
+ cwd: this.projectRoot,
58
+ ignore: ['node_modules/**', 'dist/**', '.nuxt/**', '.output/**'],
59
+ })
60
+
61
+ // Add all TypeScript files to the project
62
+ if (this.project) {
63
+ for (const file of tsFiles) {
64
+ const fullPath = join(this.projectRoot, file)
65
+ this.project.addSourceFileAtPath(fullPath)
66
+ }
67
+ }
68
+
69
+ // Extract <script> content from Vue files and add as virtual TypeScript files
70
+ if (this.project) {
71
+ for (const file of vueFiles) {
72
+ try {
73
+ const fullPath = join(this.projectRoot, file)
74
+ const content = readFileSync(fullPath, 'utf-8')
75
+ const scriptMatch = content.match(/<script[^>]*lang=["']ts["'][^>]*>([\s\S]*?)<\/script>/i)
76
+
77
+ if (scriptMatch) {
78
+ const scriptContent = scriptMatch[1]
79
+ // Create a virtual .ts file for the Vue script content
80
+ const virtualPath = fullPath.replace('.vue', '.vue.ts')
81
+ this.project.createSourceFile(virtualPath, scriptContent, { overwrite: true })
82
+ }
83
+ }
84
+ catch (error: any) {
85
+ console.error(`${colors.gray}[tela/build] ${colors.orange}✗${colors.gray} Failed to process Vue file ${file}: ${error.message}${colors.reset}`)
86
+ }
87
+ }
88
+ }
89
+
90
+ // Build type registry
91
+ if (this.project) {
92
+ this.buildTypeRegistry()
93
+ }
94
+ this.initialized = true
95
+
96
+ // Remove verbose type count message
97
+ }
98
+
99
+ private buildTypeRegistry(): void {
100
+ if (!this.project)
101
+ return
102
+ for (const sourceFile of this.project.getSourceFiles()) {
103
+ this.extractTypesFromFile(sourceFile)
104
+ }
105
+ }
106
+
107
+ private extractTypesFromFile(sourceFile: SourceFile): void {
108
+ const isVueVirtual = sourceFile.getFilePath().endsWith('.vue.ts')
109
+
110
+ // Extract type aliases
111
+ for (const typeAlias of sourceFile.getTypeAliases()) {
112
+ if (typeAlias.isExported() || isVueVirtual) {
113
+ const resolved = this.resolveTypeAlias(typeAlias)
114
+ if (resolved) {
115
+ this.typeRegistry.set(resolved.name, resolved)
116
+ }
117
+ }
118
+ }
119
+
120
+ // Extract interfaces
121
+ for (const interfaceDecl of sourceFile.getInterfaces()) {
122
+ if (interfaceDecl.isExported() || isVueVirtual) {
123
+ const resolved = this.resolveInterface(interfaceDecl)
124
+ if (resolved) {
125
+ this.typeRegistry.set(resolved.name, resolved)
126
+ }
127
+ }
128
+ }
129
+
130
+ // Extract enums
131
+ for (const enumDecl of sourceFile.getEnums()) {
132
+ if (enumDecl.isExported() || isVueVirtual) {
133
+ const resolved = this.resolveEnum(enumDecl)
134
+ if (resolved) {
135
+ this.typeRegistry.set(resolved.name, resolved)
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ private resolveTypeAlias(typeAlias: TypeAliasDeclaration): ResolvedType | null {
142
+ try {
143
+ const name = typeAlias.getName()
144
+ const typeNode = typeAlias.getTypeNode()
145
+
146
+ if (!typeNode)
147
+ return null
148
+
149
+ const definition = this.formatTypeNode(typeNode)
150
+ const raw = typeAlias.getText()
151
+
152
+ return { name, definition, raw }
153
+ }
154
+ catch {
155
+ // Silently skip failed type alias resolution
156
+ return null
157
+ }
158
+ }
159
+
160
+ private resolveInterface(interfaceDecl: InterfaceDeclaration): ResolvedType | null {
161
+ try {
162
+ const name = interfaceDecl.getName()
163
+ const properties: string[] = []
164
+
165
+ for (const prop of interfaceDecl.getProperties()) {
166
+ const propName = prop.getName()
167
+ const propType = prop.getTypeNode()
168
+ const isOptional = prop.hasQuestionToken()
169
+
170
+ const typeStr = propType ? this.formatTypeNode(propType) : 'any'
171
+ const optionalMark = isOptional ? '?' : ''
172
+
173
+ // Add comment if available
174
+ const comment = this.extractComment(prop)
175
+ const commentStr = comment ? ` // ${comment}` : ''
176
+
177
+ properties.push(` ${propName}${optionalMark}: ${typeStr}${commentStr}`)
178
+ }
179
+
180
+ const definition = `{\n${properties.join('\n')}\n}`
181
+ const raw = interfaceDecl.getText()
182
+
183
+ return { name, definition, raw }
184
+ }
185
+ catch {
186
+ // Silently skip failed interface resolution
187
+ return null
188
+ }
189
+ }
190
+
191
+ private resolveEnum(enumDecl: EnumDeclaration): ResolvedType | null {
192
+ try {
193
+ const name = enumDecl.getName()
194
+ const members = enumDecl.getMembers()
195
+
196
+ const values = members.map((member) => {
197
+ const memberName = member.getName()
198
+ const initializer = member.getInitializer()
199
+
200
+ if (initializer) {
201
+ const value = initializer.getText()
202
+ return value.startsWith('"') || value.startsWith('\'') ? value : `'${memberName}'`
203
+ }
204
+
205
+ return `'${memberName}'`
206
+ })
207
+
208
+ const definition = values.join(' | ')
209
+ const raw = enumDecl.getText()
210
+
211
+ return { name, definition, raw }
212
+ }
213
+ catch {
214
+ // Silently skip failed enum resolution
215
+ return null
216
+ }
217
+ }
218
+
219
+ private formatTypeNode(typeNode: Node): string {
220
+ const text = typeNode.getText()
221
+
222
+ // Handle array types
223
+ if (text.includes('Array<') || text.endsWith('[]')) {
224
+ return text
225
+ }
226
+
227
+ // Handle union types
228
+ if (text.includes(' | ')) {
229
+ return text
230
+ }
231
+
232
+ // Handle object types
233
+ if (text.startsWith('{') && text.endsWith('}')) {
234
+ return text
235
+ }
236
+
237
+ // Handle function types
238
+ if (text.includes('=>') || text.includes('function')) {
239
+ return text
240
+ }
241
+
242
+ // Default: return as-is
243
+ return text
244
+ }
245
+
246
+ private extractComment(node: Node): string {
247
+ const jsDocTags = (node as any).getJsDocs()
248
+ if (jsDocTags.length > 0) {
249
+ const firstDoc = jsDocTags[0]
250
+ const description = firstDoc.getDescription()
251
+ return description.trim()
252
+ }
253
+ return ''
254
+ }
255
+
256
+ public resolveType(typeName: string): string {
257
+ return this.resolveTypeInternal(typeName, new Set<string>())
258
+ }
259
+
260
+ private resolveTypeInternal(typeName: string, seen: Set<string>): string {
261
+ if (!typeName)
262
+ return 'any'
263
+
264
+ const trimmed = typeName.trim()
265
+ if (!trimmed)
266
+ return 'any'
267
+
268
+ if (seen.has(trimmed)) {
269
+ return trimmed
270
+ }
271
+ seen.add(trimmed)
272
+
273
+ const direct = this.typeRegistry.get(trimmed)
274
+ if (direct) {
275
+ return direct.definition
276
+ }
277
+
278
+ const arraySuffixMatch = trimmed.match(/^(.*)\[\]$/)
279
+ if (arraySuffixMatch) {
280
+ const elementType = arraySuffixMatch[1].trim()
281
+ const resolvedElement = this.resolveTypeInternal(elementType, seen)
282
+ return this.formatArrayType(resolvedElement)
283
+ }
284
+
285
+ if (trimmed.startsWith('Array<') && trimmed.endsWith('>')) {
286
+ const inner = trimmed.slice(6, -1).trim()
287
+ const resolvedInner = this.resolveTypeInternal(inner, seen)
288
+ return this.formatArrayType(resolvedInner)
289
+ }
290
+
291
+ const unionParts = this.splitTopLevel(trimmed, '|')
292
+ if (unionParts.length > 1) {
293
+ return unionParts
294
+ .map(part => this.resolveTypeInternal(part.trim(), seen))
295
+ .join(' | ')
296
+ }
297
+
298
+ const intersectionParts = this.splitTopLevel(trimmed, '&')
299
+ if (intersectionParts.length > 1) {
300
+ return intersectionParts
301
+ .map(part => this.resolveTypeInternal(part.trim(), seen))
302
+ .join(' & ')
303
+ }
304
+
305
+ const genericIndex = trimmed.indexOf('<')
306
+ if (genericIndex > 0 && trimmed.endsWith('>')) {
307
+ const base = trimmed.slice(0, genericIndex)
308
+ const inner = trimmed.slice(genericIndex + 1, -1)
309
+ const genericParts = this.splitTopLevel(inner, ',').map(part => part.trim()).filter(Boolean)
310
+ if (genericParts.length > 0) {
311
+ const resolvedParts = genericParts.map(part => this.resolveTypeInternal(part, seen))
312
+ return `${base}<${resolvedParts.join(', ')}>`
313
+ }
314
+ }
315
+
316
+ if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
317
+ return this.resolveTypeInternal(trimmed.slice(1, -1), seen)
318
+ }
319
+
320
+ switch (trimmed) {
321
+ case 'TSIndexedAccessType':
322
+ return 'string'
323
+ case 'TSTypeReference':
324
+ return 'any'
325
+ case 'TSFunctionType':
326
+ return '(...args: any[]) => any'
327
+ case 'TSUnionType':
328
+ case 'union':
329
+ return 'string'
330
+ default:
331
+ return trimmed
332
+ }
333
+ }
334
+
335
+ public hasType(typeName: string): boolean {
336
+ return this.typeRegistry.has(typeName)
337
+ }
338
+
339
+ public getAllTypes(): Map<string, ResolvedType> {
340
+ return new Map(this.typeRegistry)
341
+ }
342
+
343
+ private formatArrayType(resolvedElement: string): string {
344
+ const value = resolvedElement.trim()
345
+ if (!value) {
346
+ return 'any[]'
347
+ }
348
+ if (value.startsWith('{') && value.endsWith('}')) {
349
+ return `${value}[]`
350
+ }
351
+ return `Array<${value}>`
352
+ }
353
+
354
+ private splitTopLevel(input: string, delimiter: string): string[] {
355
+ const parts: string[] = []
356
+ let depth = 0
357
+ let buffer = ''
358
+
359
+ const openers = new Map([
360
+ ['<', '>'],
361
+ ['(', ')'],
362
+ ['[', ']'],
363
+ ['{', '}'],
364
+ ])
365
+ const closers = new Map(Array.from(openers.entries()).map(([o, c]) => [c, o]))
366
+
367
+ for (let i = 0; i < input.length; i++) {
368
+ const char = input[i]
369
+ if (openers.has(char)) {
370
+ depth++
371
+ }
372
+ else if (closers.has(char)) {
373
+ depth = Math.max(0, depth - 1)
374
+ }
375
+
376
+ if (char === delimiter && depth === 0) {
377
+ parts.push(buffer.trim())
378
+ buffer = ''
379
+ continue
380
+ }
381
+
382
+ buffer += char
383
+ }
384
+
385
+ if (buffer.trim()) {
386
+ parts.push(buffer.trim())
387
+ }
388
+
389
+ return parts
390
+ }
391
+ }
392
+
393
+ // Global instance
394
+ let globalResolver: TypeResolver | null = null
395
+
396
+ export async function getTypeResolver(projectRoot: string, appRootDir?: string): Promise<TypeResolver> {
397
+ if (!globalResolver) {
398
+ globalResolver = new TypeResolver(projectRoot, appRootDir)
399
+ await globalResolver.initialize()
400
+ }
401
+ return globalResolver
402
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }