vueless 1.0.2-beta.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +1 -0
  2. package/bin/commands/copy.js +2 -2
  3. package/bin/commands/create.js +2 -2
  4. package/bin/commands/init.js +64 -5
  5. package/bin/constants.js +27 -17
  6. package/composables/tests/useUI.test.ts +494 -0
  7. package/constants.js +7 -2
  8. package/directives/clickOutside/storybook/docs.mdx +0 -1
  9. package/directives/clickOutside/storybook/stories.ts +22 -16
  10. package/directives/tooltip/storybook/docs.mdx +0 -1
  11. package/directives/tooltip/storybook/stories.ts +21 -14
  12. package/icons/internal/home.svg +1 -0
  13. package/icons/storybook/account_balance.svg +1 -0
  14. package/icons/storybook/arrow_back.svg +1 -0
  15. package/icons/storybook/arrow_forward.svg +1 -0
  16. package/icons/storybook/asterisk.svg +1 -0
  17. package/icons/storybook/brightness_1.svg +1 -0
  18. package/icons/storybook/celebration.svg +1 -0
  19. package/icons/storybook/contract.svg +1 -0
  20. package/icons/storybook/cookie.svg +1 -0
  21. package/icons/storybook/credit_card.svg +1 -0
  22. package/icons/storybook/description.svg +1 -0
  23. package/icons/storybook/directions_bike.svg +1 -0
  24. package/icons/storybook/directions_car.svg +1 -0
  25. package/icons/storybook/done_all.svg +1 -0
  26. package/icons/storybook/east.svg +1 -0
  27. package/icons/storybook/handyman.svg +1 -0
  28. package/icons/storybook/keyboard_tab.svg +1 -0
  29. package/icons/storybook/keyboard_tab_rtl.svg +1 -0
  30. package/icons/storybook/notifications.svg +1 -0
  31. package/icons/storybook/question_mark.svg +1 -0
  32. package/icons/storybook/schedule.svg +1 -0
  33. package/icons/storybook/straighten.svg +1 -0
  34. package/icons/storybook/trending_up.svg +1 -0
  35. package/package.json +29 -30
  36. package/plugin-vite.js +52 -28
  37. package/tailwind.css +2 -8
  38. package/ui.boilerplate/UBoilerplate.vue +2 -2
  39. package/ui.boilerplate/storybook/stories.hidden.ts +1 -1
  40. package/ui.boilerplate/tests/UBoilerplate.test.ts +60 -0
  41. package/ui.button/storybook/stories.ts +2 -2
  42. package/ui.button/tests/UButton.test.ts +31 -53
  43. package/ui.button-link/ULink.vue +4 -4
  44. package/ui.button-link/storybook/stories.ts +1 -1
  45. package/ui.button-link/tests/ULink.test.ts +23 -51
  46. package/ui.button-toggle/config.ts +1 -1
  47. package/ui.button-toggle/storybook/stories.ts +4 -3
  48. package/ui.button-toggle/tests/UToggle.test.ts +76 -102
  49. package/ui.container-accordion/storybook/stories.ts +9 -12
  50. package/ui.container-accordion/tests/UAccordion.test.ts +248 -0
  51. package/ui.container-card/UCard.vue +5 -2
  52. package/ui.container-card/storybook/stories.ts +123 -81
  53. package/ui.container-card/tests/UCard.test.ts +229 -0
  54. package/ui.container-col/UCol.vue +4 -4
  55. package/ui.container-col/config.ts +2 -0
  56. package/ui.container-col/storybook/stories.ts +14 -10
  57. package/ui.container-col/tests/UCol.test.ts +207 -0
  58. package/ui.container-col/types.ts +5 -0
  59. package/ui.container-divider/storybook/stories.ts +23 -16
  60. package/ui.container-divider/tests/UDivider.test.ts +228 -0
  61. package/ui.container-group/storybook/stories.ts +60 -42
  62. package/ui.container-group/tests/UGroup.test.ts +204 -0
  63. package/ui.container-groups/storybook/stories.ts +41 -12
  64. package/ui.container-groups/tests/UGroups.test.ts +75 -0
  65. package/ui.container-modal/UModal.vue +27 -14
  66. package/ui.container-modal/config.ts +12 -4
  67. package/ui.container-modal/storybook/stories.ts +189 -55
  68. package/ui.container-modal/tests/UModal.test.ts +646 -0
  69. package/ui.container-modal-confirm/UModalConfirm.vue +10 -3
  70. package/ui.container-modal-confirm/storybook/stories.ts +57 -22
  71. package/ui.container-modal-confirm/tests/UModalConfirm.test.ts +506 -0
  72. package/ui.container-page/UPage.vue +5 -2
  73. package/ui.container-page/config.ts +1 -1
  74. package/ui.container-page/storybook/stories.ts +170 -80
  75. package/ui.container-page/tests/UPage.test.ts +331 -0
  76. package/ui.container-row/URow.vue +4 -4
  77. package/ui.container-row/config.ts +1 -0
  78. package/ui.container-row/storybook/stories.ts +4 -3
  79. package/ui.container-row/tests/URow.test.ts +205 -0
  80. package/ui.container-row/types.ts +5 -0
  81. package/ui.data-list/UDataList.vue +2 -13
  82. package/ui.data-list/config.ts +0 -1
  83. package/ui.data-list/storybook/docs.mdx +3 -4
  84. package/ui.data-list/storybook/stories.ts +89 -71
  85. package/ui.data-list/tests/UDataList.test.ts +391 -0
  86. package/ui.data-list/types.ts +0 -6
  87. package/ui.data-table/UTable.vue +2 -2
  88. package/ui.data-table/UTableRow.vue +7 -7
  89. package/ui.data-table/config.ts +2 -2
  90. package/ui.data-table/storybook/docs.mdx +23 -9
  91. package/ui.data-table/storybook/stories.ts +374 -182
  92. package/ui.data-table/tests/UTable.test.ts +612 -0
  93. package/ui.data-table/tests/UTableRow.test.ts +545 -0
  94. package/ui.data-table/types.ts +2 -2
  95. package/ui.dropdown-badge/UDropdownBadge.vue +13 -2
  96. package/ui.dropdown-badge/config.ts +3 -12
  97. package/ui.dropdown-badge/storybook/stories.ts +5 -4
  98. package/ui.dropdown-badge/tests/UDropdownBadge.test.ts +421 -0
  99. package/ui.dropdown-button/UDropdownButton.vue +13 -2
  100. package/ui.dropdown-button/config.ts +4 -10
  101. package/ui.dropdown-button/storybook/stories.ts +9 -2
  102. package/ui.dropdown-button/tests/UDropdownButton.test.ts +449 -0
  103. package/ui.dropdown-link/UDropdownLink.vue +8 -1
  104. package/ui.dropdown-link/config.ts +6 -12
  105. package/ui.dropdown-link/storybook/stories.ts +15 -4
  106. package/ui.dropdown-link/tests/UDropdownLink.test.ts +454 -0
  107. package/ui.form-calendar/UCalendar.vue +107 -57
  108. package/ui.form-calendar/UCalendarDayView.vue +60 -21
  109. package/ui.form-calendar/UCalendarMonthView.vue +10 -14
  110. package/ui.form-calendar/UCalendarYearView.vue +33 -52
  111. package/ui.form-calendar/config.ts +3 -3
  112. package/ui.form-calendar/constants.ts +2 -0
  113. package/ui.form-calendar/storybook/stories.ts +40 -48
  114. package/ui.form-calendar/tests/UCalendar.test.ts +656 -0
  115. package/ui.form-calendar/tests/UCalendarDayView.test.ts +401 -0
  116. package/ui.form-calendar/tests/UCalendarMonthView.test.ts +214 -0
  117. package/ui.form-calendar/tests/UCalendarYearView.test.ts +226 -0
  118. package/ui.form-calendar/types.ts +2 -2
  119. package/ui.form-calendar/utilCalendar.ts +3 -7
  120. package/ui.form-checkbox/UCheckbox.vue +20 -5
  121. package/ui.form-checkbox/config.ts +4 -2
  122. package/ui.form-checkbox/storybook/stories.ts +30 -41
  123. package/ui.form-checkbox/tests/UCheckbox.test.ts +314 -0
  124. package/ui.form-checkbox/types.ts +1 -1
  125. package/ui.form-checkbox-group/UCheckboxGroup.vue +3 -3
  126. package/ui.form-checkbox-group/storybook/stories.ts +50 -26
  127. package/ui.form-checkbox-group/tests/UCheckboxGroup.test.ts +267 -0
  128. package/ui.form-checkbox-multi-state/UCheckboxMultiState.vue +2 -1
  129. package/ui.form-checkbox-multi-state/storybook/stories.ts +2 -2
  130. package/ui.form-checkbox-multi-state/tests/UCheckboxMultiState.test.ts +224 -0
  131. package/ui.form-color-picker/UColorPicker.vue +4 -3
  132. package/ui.form-color-picker/storybook/stories.ts +19 -19
  133. package/ui.form-color-picker/tests/UColorPicker.test.ts +142 -0
  134. package/ui.form-date-picker/UDatePicker.vue +4 -1
  135. package/ui.form-date-picker/storybook/stories.ts +178 -53
  136. package/ui.form-date-picker/tests/UDatePicker.test.ts +518 -0
  137. package/ui.form-date-picker-range/UDatePickerRange.vue +39 -20
  138. package/ui.form-date-picker-range/UDatePickerRangePeriodMenu.vue +4 -2
  139. package/ui.form-date-picker-range/storybook/stories.ts +93 -51
  140. package/ui.form-date-picker-range/tests/UDatePickerRange.test.ts +574 -0
  141. package/ui.form-date-picker-range/types.ts +1 -0
  142. package/ui.form-input/UInput.vue +27 -11
  143. package/ui.form-input/config.ts +18 -9
  144. package/ui.form-input/storybook/stories.ts +71 -22
  145. package/ui.form-input/tests/UInput.test.ts +606 -0
  146. package/ui.form-input/types.ts +2 -2
  147. package/ui.form-input-counter/UInputCounter.vue +5 -2
  148. package/ui.form-input-counter/config.ts +3 -2
  149. package/ui.form-input-counter/storybook/stories.ts +1 -1
  150. package/ui.form-input-counter/tests/UInputCounter.test.ts +268 -0
  151. package/ui.form-input-file/UInputFile.vue +4 -3
  152. package/ui.form-input-file/config.ts +2 -2
  153. package/ui.form-input-file/storybook/stories.ts +38 -19
  154. package/ui.form-input-file/tests/UInputFile.test.ts +511 -0
  155. package/ui.form-input-number/UInputNumber.vue +3 -3
  156. package/ui.form-input-number/storybook/stories.ts +60 -17
  157. package/ui.form-input-number/tests/UInputNumber.test.ts +454 -0
  158. package/ui.form-input-number/useFormatNumber.ts +1 -0
  159. package/ui.form-input-password/UInputPassword.vue +9 -0
  160. package/ui.form-input-password/storybook/stories.ts +65 -21
  161. package/ui.form-input-password/tests/UInputPassword.test.ts +303 -0
  162. package/ui.form-input-rating/UInputRating.vue +1 -1
  163. package/ui.form-input-rating/storybook/stories.ts +16 -4
  164. package/ui.form-input-rating/tests/UInputRating.test.ts +272 -0
  165. package/ui.form-input-search/config.ts +1 -1
  166. package/ui.form-input-search/storybook/stories.ts +66 -24
  167. package/ui.form-input-search/tests/UInputSearch.test.ts +372 -0
  168. package/ui.form-label/ULabel.vue +12 -15
  169. package/ui.form-label/config.ts +8 -9
  170. package/ui.form-label/storybook/stories.ts +31 -15
  171. package/ui.form-label/tests/ULabel.test.ts +374 -0
  172. package/ui.form-label/types.ts +0 -5
  173. package/ui.form-listbox/UListbox.vue +39 -43
  174. package/ui.form-listbox/config.ts +15 -11
  175. package/ui.form-listbox/storybook/stories.ts +18 -10
  176. package/ui.form-listbox/tests/UListbox.test.ts +811 -0
  177. package/ui.form-radio/URadio.vue +2 -5
  178. package/ui.form-radio/config.ts +2 -2
  179. package/ui.form-radio/storybook/stories.ts +42 -20
  180. package/ui.form-radio/tests/URadio.test.ts +226 -0
  181. package/ui.form-radio-group/storybook/stories.ts +21 -21
  182. package/ui.form-radio-group/tests/URadioGroup.test.ts +277 -0
  183. package/ui.form-select/USelect.vue +50 -23
  184. package/ui.form-select/config.ts +12 -16
  185. package/ui.form-select/storybook/assets/images/alex-johnson.png +0 -0
  186. package/ui.form-select/storybook/assets/images/emily-davis.png +0 -0
  187. package/ui.form-select/storybook/assets/images/john-doe.png +0 -0
  188. package/ui.form-select/storybook/assets/images/pat-morgan.png +0 -0
  189. package/ui.form-select/storybook/stories.ts +392 -152
  190. package/ui.form-select/tests/USelect.test.ts +824 -0
  191. package/ui.form-select/types.ts +5 -0
  192. package/ui.form-switch/USwitch.vue +0 -1
  193. package/ui.form-switch/storybook/stories.ts +18 -17
  194. package/ui.form-switch/tests/USwitch.test.ts +239 -0
  195. package/ui.form-textarea/UTextarea.vue +76 -60
  196. package/ui.form-textarea/config.ts +22 -9
  197. package/ui.form-textarea/storybook/stories.ts +30 -19
  198. package/ui.form-textarea/tests/UTextarea.test.ts +546 -0
  199. package/ui.form-textarea/types.ts +13 -8
  200. package/ui.image-avatar/config.ts +1 -1
  201. package/ui.image-avatar/storybook/stories.ts +1 -1
  202. package/ui.image-avatar/tests/UAvatar.test.ts +240 -0
  203. package/ui.image-icon/UIcon.vue +9 -1
  204. package/ui.image-icon/config.ts +1 -0
  205. package/ui.image-icon/storybook/stories.ts +2 -10
  206. package/ui.image-icon/tests/UIcon.test.ts +224 -0
  207. package/ui.image-icon/tests/assets/test-icon.svg +1 -0
  208. package/ui.loader/storybook/stories.ts +1 -8
  209. package/ui.loader/tests/ULoader.test.ts +146 -0
  210. package/ui.loader-overlay/storybook/stories.ts +10 -4
  211. package/ui.loader-overlay/tests/ULoaderOverlay.test.ts +168 -0
  212. package/ui.loader-progress/storybook/stories.ts +6 -6
  213. package/ui.loader-progress/tests/ULoaderProgress.test.ts +165 -0
  214. package/ui.navigation-breadcrumbs/UBreadcrumbs.vue +17 -4
  215. package/ui.navigation-breadcrumbs/config.ts +1 -0
  216. package/ui.navigation-breadcrumbs/storybook/stories.ts +36 -34
  217. package/ui.navigation-breadcrumbs/tests/UBreadcrumbs.test.ts +439 -0
  218. package/ui.navigation-breadcrumbs/types.ts +2 -2
  219. package/ui.navigation-pagination/UPagination.vue +1 -1
  220. package/ui.navigation-pagination/storybook/stories.ts +27 -43
  221. package/ui.navigation-pagination/tests/UPagination.test.ts +442 -0
  222. package/ui.navigation-progress/UProgress.vue +8 -3
  223. package/ui.navigation-progress/UStepperProgress.vue +8 -3
  224. package/ui.navigation-progress/storybook/stories.ts +7 -5
  225. package/ui.navigation-progress/tests/UProgress.test.ts +250 -0
  226. package/ui.navigation-tab/UTab.vue +1 -1
  227. package/ui.navigation-tab/config.ts +6 -1
  228. package/ui.navigation-tab/storybook/assets/john-doe.png +0 -0
  229. package/ui.navigation-tab/storybook/stories.ts +12 -5
  230. package/ui.navigation-tab/tests/UTab.test.ts +486 -0
  231. package/ui.navigation-tabs/storybook/stories.ts +19 -16
  232. package/ui.navigation-tabs/tests/UTabs.test.ts +339 -0
  233. package/ui.other-chip/UChip.vue +2 -2
  234. package/ui.other-chip/config.ts +12 -1
  235. package/ui.other-chip/storybook/stories.ts +4 -4
  236. package/ui.other-chip/tests/UChip.test.ts +254 -0
  237. package/ui.other-chip/types.ts +1 -1
  238. package/ui.other-dot/config.ts +0 -3
  239. package/ui.other-dot/storybook/stories.ts +1 -8
  240. package/ui.other-dot/tests/UDot.test.ts +83 -0
  241. package/ui.other-dot/types.ts +1 -1
  242. package/ui.other-theme-color-toggle/UThemeColorToggle.vue +10 -3
  243. package/ui.other-theme-color-toggle/storybook/stories.ts +1 -1
  244. package/ui.other-theme-color-toggle/tests/UThemeColorToggle.test.ts +249 -0
  245. package/ui.skeleton/USkeleton.vue +1 -0
  246. package/ui.skeleton/storybook/stories.ts +7 -12
  247. package/ui.skeleton/tests/USkeleton.test.ts +61 -0
  248. package/ui.skeleton-choice/USkeletonChoice.vue +1 -0
  249. package/ui.skeleton-choice/config.ts +15 -19
  250. package/ui.skeleton-choice/storybook/stories.ts +19 -22
  251. package/ui.skeleton-choice/tests/USkeletonChoice.test.ts +194 -0
  252. package/ui.skeleton-input/USkeletonInput.vue +10 -2
  253. package/ui.skeleton-input/config.ts +36 -46
  254. package/ui.skeleton-input/storybook/stories.ts +32 -31
  255. package/ui.skeleton-input/tests/USkeletonInput.test.ts +256 -0
  256. package/ui.skeleton-text/USkeletonText.vue +2 -2
  257. package/ui.skeleton-text/config.ts +1 -1
  258. package/ui.skeleton-text/storybook/stories.ts +12 -25
  259. package/ui.skeleton-text/tests/USkeletonText.test.ts +130 -0
  260. package/ui.text-alert/UAlert.vue +2 -2
  261. package/ui.text-alert/config.ts +1 -1
  262. package/ui.text-alert/storybook/stories.ts +61 -32
  263. package/ui.text-alert/tests/UAlert.test.ts +331 -0
  264. package/ui.text-badge/UBadge.vue +3 -1
  265. package/ui.text-badge/storybook/stories.ts +14 -24
  266. package/ui.text-badge/types.ts +5 -0
  267. package/ui.text-block/UText.vue +4 -4
  268. package/ui.text-block/config.ts +30 -1
  269. package/ui.text-block/storybook/stories.ts +88 -37
  270. package/ui.text-block/tests/UText.test.ts +74 -11
  271. package/ui.text-block/types.ts +33 -2
  272. package/ui.text-empty/config.ts +1 -1
  273. package/ui.text-empty/storybook/assets/empty-inbox.png +0 -0
  274. package/ui.text-empty/storybook/stories.ts +19 -11
  275. package/ui.text-file/UFile.vue +24 -22
  276. package/ui.text-file/config.ts +2 -2
  277. package/ui.text-file/storybook/assets/imageURL.png +0 -0
  278. package/ui.text-file/storybook/stories.ts +19 -11
  279. package/ui.text-file/tests/UFile.test.ts +257 -0
  280. package/ui.text-files/UFiles.vue +22 -22
  281. package/ui.text-files/config.ts +0 -1
  282. package/ui.text-files/storybook/stories.ts +16 -15
  283. package/ui.text-files/tests/UFiles.test.ts +307 -0
  284. package/ui.text-files/types.ts +0 -5
  285. package/ui.text-header/UHeader.vue +1 -0
  286. package/ui.text-header/config.ts +25 -2
  287. package/ui.text-header/storybook/stories.ts +29 -9
  288. package/ui.text-header/tests/UHeader.test.ts +46 -1
  289. package/ui.text-header/types.ts +13 -1
  290. package/ui.text-notify/UNotify.vue +22 -9
  291. package/ui.text-notify/constants.ts +0 -1
  292. package/ui.text-notify/storybook/docs.mdx +16 -16
  293. package/ui.text-notify/storybook/stories.ts +264 -52
  294. package/ui.text-notify/tests/UNotify.test.ts +276 -0
  295. package/ui.text-notify/types.ts +13 -0
  296. package/ui.text-notify/utilNotify.ts +41 -30
  297. package/ui.text-number/storybook/stories.ts +31 -52
  298. package/ui.text-number/tests/UNumber.test.ts +351 -0
  299. package/utils/helper.ts +12 -1
  300. package/utils/node/dynamicProps.js +8 -2
  301. package/utils/node/helper.js +71 -1
  302. package/utils/node/loaderIcon.js +54 -43
  303. package/utils/node/tailwindSafelist.js +3 -1
  304. package/utils/node/vuelessConfig.js +4 -1
  305. package/utils/storybook.ts +5 -1
  306. package/icons/storybook/heart_plus.svg +0 -1
  307. package/icons/storybook/label.svg +0 -1
  308. package/icons/storybook/warning.svg +0 -1
  309. /package/bin/utils/{dataUtils.js → data.js} +0 -0
  310. /package/bin/utils/{formatUtil.js → format.js} +0 -0
package/README.md CHANGED
@@ -26,6 +26,7 @@ Vueless is simple enough for everyday use and powerful enough for advanced scena
26
26
  - ♿️ Accessibility (a11y)
27
27
  - ⚙️ Server-side rendering (SSR) friendly
28
28
  - 🖼️ 1000+ built-in SVG icons
29
+ - 🧪️ 1200+ unit tests ensuring consistent logic
29
30
  - 🛡️ Full TypeScript support with type safety
30
31
 
31
32
  ### Advanced Features
@@ -7,8 +7,8 @@ import { cp, readFile, writeFile, rename } from "node:fs/promises";
7
7
  import { styleText } from "node:util";
8
8
 
9
9
  import { getDirFiles } from "../../utils/node/helper.js";
10
- import { replaceRelativeImports } from "../utils/formatUtil.js";
11
- import { getStorybookId, getStoryMetaKeyIndex } from "../utils/dataUtils.js";
10
+ import { replaceRelativeImports } from "../utils/format.js";
11
+ import { getStorybookId, getStoryMetaKeyIndex } from "../utils/data.js";
12
12
 
13
13
  import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
14
14
  import { COMPONENTS, VUELESS_PACKAGE_DIR, VUELESS_LOCAL_DIR } from "../../constants.js";
@@ -7,8 +7,8 @@ import { readFile, writeFile, rename, mkdir, readdir, copyFile } from "node:fs/p
7
7
  import { styleText } from "node:util";
8
8
 
9
9
  import { getDirFiles } from "../../utils/node/helper.js";
10
- import { replaceRelativeImports } from "../utils/formatUtil.js";
11
- import { getStorybookId } from "../utils/dataUtils.js";
10
+ import { replaceRelativeImports } from "../utils/format.js";
11
+ import { getStorybookId } from "../utils/data.js";
12
12
 
13
13
  import { SRC_COMPONENTS_PATH, COMPONENTS_PATH } from "../constants.js";
14
14
 
@@ -2,15 +2,32 @@
2
2
 
3
3
  import { cwd } from "node:process";
4
4
  import path from "node:path";
5
- import { existsSync } from "node:fs";
6
- import { writeFile, rename } from "node:fs/promises";
5
+ import { existsSync, mkdirSync } from "node:fs";
6
+ import { writeFile, rename, readFile } from "node:fs/promises";
7
7
  import { styleText } from "node:util";
8
8
 
9
- import { DEFAULT_VUELESS_CONFIG_CONTENT } from "../constants.js";
10
- import { JAVASCRIPT_EXT, TYPESCRIPT_EXT, VUELESS_CONFIG_FILE_NAME } from "../../constants.js";
9
+ import {
10
+ SUPPRESS_TS_CHECK,
11
+ COMPONENTS_INDEX_EXPORT,
12
+ COMPONENTS_INDEX_COMMENT,
13
+ DEFAULT_VUELESS_CONFIG_CONTENT,
14
+ } from "../constants.js";
15
+
16
+ import {
17
+ JAVASCRIPT_EXT,
18
+ TYPESCRIPT_EXT,
19
+ CONFIG_INDEX_FILE_NAME,
20
+ VUELESS_CONFIG_FILE_NAME,
21
+ } from "../../constants.js";
11
22
 
12
23
  const vuelessInitOptions = ["--ts", "--js"];
13
24
 
25
+ /**
26
+ * Initializes Vueless in the project by creating a default config file and .vueless directory.
27
+ * @param {string[]} options - The function options.
28
+ * @param {boolean} options.includes("--ts") - If true, creates a TypeScript config file.
29
+ * @param {boolean} options.includes("--js") - If true, creates a JavaScript config file.
30
+ */
14
31
  export async function vuelessInit(options) {
15
32
  const isValidOptions = options.every((option) => vuelessInitOptions.includes(option));
16
33
 
@@ -20,13 +37,16 @@ export async function vuelessInit(options) {
20
37
  return;
21
38
  }
22
39
 
23
- const fileExt = options.includes("--ts") ? TYPESCRIPT_EXT : JAVASCRIPT_EXT;
40
+ const hasTypeScript = await detectTypeScript();
41
+ const fileExt = options.includes("--ts") || hasTypeScript ? TYPESCRIPT_EXT : JAVASCRIPT_EXT;
42
+
24
43
  const formattedDestPath = path.format({
25
44
  dir: cwd(),
26
45
  name: VUELESS_CONFIG_FILE_NAME,
27
46
  ext: fileExt,
28
47
  });
29
48
 
49
+ /* Backup existing config if it exists. */
30
50
  if (existsSync(formattedDestPath)) {
31
51
  const timestamp = new Date().valueOf();
32
52
  const renamedTarget = `${VUELESS_CONFIG_FILE_NAME}-backup-${timestamp}${fileExt}`;
@@ -42,6 +62,7 @@ export async function vuelessInit(options) {
42
62
  );
43
63
  }
44
64
 
65
+ /* Create a default config file. */
45
66
  await writeFile(formattedDestPath, DEFAULT_VUELESS_CONFIG_CONTENT, "utf-8");
46
67
 
47
68
  console.log(
@@ -50,4 +71,42 @@ export async function vuelessInit(options) {
50
71
  `The '${formattedDestPath.split(path.sep).at(-1)}' was created in the project root directory.`,
51
72
  ),
52
73
  );
74
+
75
+ /* Create .vueless directory and index file. */
76
+ const vuelessDir = path.join(cwd(), ".vueless");
77
+ const destPath = path.join(vuelessDir, `${CONFIG_INDEX_FILE_NAME}${fileExt}`);
78
+
79
+ if (!existsSync(vuelessDir)) {
80
+ mkdirSync(vuelessDir);
81
+ console.log(
82
+ styleText("green", "The '.vueless' directory was created in the project root directory."),
83
+ );
84
+ }
85
+
86
+ const suppressTsCheck = fileExt === TYPESCRIPT_EXT ? `${SUPPRESS_TS_CHECK}\n` : "";
87
+
88
+ await writeFile(
89
+ destPath,
90
+ `${suppressTsCheck}${COMPONENTS_INDEX_COMMENT}\n${COMPONENTS_INDEX_EXPORT}\n`,
91
+ "utf-8",
92
+ );
93
+ }
94
+
95
+ /**
96
+ * Detects if TypeScript is a dependency in the project's package.json
97
+ * @returns {Promise<boolean>} True if TypeScript is found in dependencies or devDependencies
98
+ */
99
+ async function detectTypeScript() {
100
+ try {
101
+ const packageJsonPath = path.join(cwd(), "package.json");
102
+ const packageJsonContent = await readFile(packageJsonPath, "utf-8");
103
+ const pkg = JSON.parse(packageJsonContent);
104
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
105
+
106
+ return Boolean(deps.typescript);
107
+ } catch (error) {
108
+ console.error("Failed to detect TypeScript:", error);
109
+
110
+ return false;
111
+ }
53
112
  }
package/bin/constants.js CHANGED
@@ -1,7 +1,8 @@
1
1
  export const SRC_COMPONENTS_PATH = "/src/components";
2
2
  export const COMPONENTS_PATH = "/components";
3
3
 
4
- export const DEFAULT_VUELESS_CONFIG_CONTENT = `
4
+ export const DEFAULT_VUELESS_CONFIG_CONTENT = `import { componentConfigs } from "./.vueless";
5
+
5
6
  export default {
6
7
  /**
7
8
  * Global settings.
@@ -14,6 +15,18 @@ export default {
14
15
  disabledOpacity: 50,
15
16
  colorMode: "auto",
16
17
 
18
+ /**
19
+ * Component settings.
20
+ */
21
+ components: /*tw*/ {
22
+ ...componentConfigs,
23
+ },
24
+
25
+ /**
26
+ * Directive settings.
27
+ */
28
+ directives: {},
29
+
17
30
  /**
18
31
  * Light theme CSS variable settings.
19
32
  */
@@ -153,22 +166,19 @@ export default {
153
166
  "--vl-bg-accented": "--vl-neutral-700",
154
167
  "--vl-bg-inverted": "--vl-neutral-100",
155
168
  },
169
+ };
170
+ `;
156
171
 
157
- /**
158
- * Directive settings.
159
- */
160
- directives: {},
172
+ export const SUPPRESS_TS_CHECK = `// eslint-disable-next-line @typescript-eslint/ban-ts-comment
173
+ // @ts-nocheck`;
161
174
 
162
- /**
163
- * Component settings.
164
- */
165
- components: /*tw*/ {},
175
+ export const COMPONENTS_INDEX_COMMENT = `/**
176
+ * ⚠️ This file is auto-generated — do not edit it manually.
177
+ * It gets updated automatically whenever the Vite server restarts.
178
+ *
179
+ * This file imports all component config files from the current directory.
180
+ * Only files following the naming pattern "U[Component].config.[ts|js]" will be included.
181
+ * Example: "UButton.config.ts"
182
+ */`;
166
183
 
167
- /**
168
- * TailwindMerge settings for custom Tailwind CSS classes.
169
- * All lists of rules available here:
170
- * https://github.com/dcastil/tailwind-merge/blob/main/src/lib/default-config.ts
171
- */
172
- tailwindMerge: {},
173
- };
174
- `;
184
+ export const COMPONENTS_INDEX_EXPORT = `export const componentConfigs = {};`;
@@ -0,0 +1,494 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { computed, nextTick } from "vue";
3
+ import { mount } from "@vue/test-utils";
4
+
5
+ // TODO: Autogenerated, need to be reviewed
6
+
7
+ // Mock the ui utils
8
+ vi.mock("../../utils/ui.ts", () => ({
9
+ cx: vi.fn((classes) => (Array.isArray(classes) ? classes.filter(Boolean).join(" ") : classes)),
10
+ cva: vi.fn((config) => {
11
+ // Return a spy function that can be called and tracked
12
+ const cvaSpy = vi.fn((props) => {
13
+ if (!config.variants) return config.base || "";
14
+
15
+ let classes = config.base || "";
16
+
17
+ // Apply variants
18
+ Object.entries(config.variants).forEach(([key, variants]) => {
19
+ const value = props[key];
20
+
21
+ if (value && variants[value]) {
22
+ classes += ` ${variants[value]}`;
23
+ }
24
+ });
25
+
26
+ return classes.trim();
27
+ });
28
+
29
+ return cvaSpy;
30
+ }),
31
+ setColor: vi.fn((classes, color) => classes?.replace(/{color}/g, color)),
32
+ vuelessConfig: { components: {}, unstyled: false },
33
+ getMergedConfig: vi.fn((args) => {
34
+ // Create a spy function that returns the expected merged config
35
+ const { defaultConfig, globalConfig, propsConfig } = args;
36
+
37
+ return {
38
+ ...defaultConfig,
39
+ ...globalConfig,
40
+ ...propsConfig,
41
+ };
42
+ }),
43
+ }));
44
+
45
+ // Mock Vue functions
46
+ vi.mock("vue", async () => {
47
+ const actual = await vi.importActual("vue");
48
+
49
+ return {
50
+ ...actual,
51
+ getCurrentInstance: vi.fn(),
52
+ useAttrs: vi.fn(),
53
+ };
54
+ });
55
+
56
+ import useUI from "../useUI.ts";
57
+ import * as uiUtils from "../../utils/ui.ts";
58
+ import { getCurrentInstance, useAttrs } from "vue";
59
+
60
+ // Test component for integration testing
61
+ const TestComponent = {
62
+ template: '<div :data-test="getDataTest()" v-bind="bodyAttrs">Test</div>',
63
+ setup() {
64
+ const defaultConfig = {
65
+ body: {
66
+ base: "base-class",
67
+ variants: {
68
+ variant: {
69
+ primary: "primary-class",
70
+ secondary: "secondary-class",
71
+ },
72
+ size: {
73
+ sm: "small-class",
74
+ md: "medium-class",
75
+ },
76
+ },
77
+ },
78
+ defaults: {
79
+ variant: "primary",
80
+ size: "md",
81
+ },
82
+ };
83
+
84
+ return useUI(defaultConfig);
85
+ },
86
+ };
87
+
88
+ describe("useUI", () => {
89
+ beforeEach(() => {
90
+ vi.clearAllMocks();
91
+ // Reset vuelessConfig
92
+ uiUtils.vuelessConfig.components = {};
93
+ uiUtils.vuelessConfig.unstyled = false;
94
+
95
+ // Setup default mocks
96
+ vi.mocked(getCurrentInstance).mockReturnValue({
97
+ type: { __name: "TestComponent" },
98
+ props: { dataTest: "test", color: "primary", config: {} },
99
+ parent: null,
100
+ });
101
+
102
+ vi.mocked(useAttrs).mockReturnValue({
103
+ class: "",
104
+ style: "",
105
+ });
106
+ });
107
+
108
+ afterEach(() => {
109
+ vi.restoreAllMocks();
110
+ });
111
+
112
+ describe("Basic Functionality", () => {
113
+ it("should return config, getDataTest, and attribute objects", () => {
114
+ const defaultConfig = {
115
+ body: { base: "test-class" },
116
+ defaults: { variant: "primary" },
117
+ };
118
+
119
+ const result = useUI(defaultConfig);
120
+
121
+ expect(result).toHaveProperty("config");
122
+ expect(result).toHaveProperty("getDataTest");
123
+ expect(result).toHaveProperty("bodyAttrs");
124
+ expect(typeof result.getDataTest).toBe("function");
125
+ });
126
+
127
+ it("should merge default config with global and props config", async () => {
128
+ const defaultConfig = {
129
+ body: { base: "default-class" },
130
+ defaults: { variant: "primary" },
131
+ };
132
+
133
+ const globalConfig = {
134
+ body: { base: "global-class" },
135
+ };
136
+
137
+ const propsConfig = {
138
+ body: { base: "props-class" },
139
+ };
140
+
141
+ // Set global config
142
+ uiUtils.vuelessConfig.components = {
143
+ TestComponent: globalConfig,
144
+ };
145
+
146
+ // Mock getCurrentInstance to return props config
147
+ vi.mocked(getCurrentInstance).mockReturnValue({
148
+ type: { __name: "TestComponent" },
149
+ props: { config: propsConfig, dataTest: "test" },
150
+ parent: null,
151
+ });
152
+
153
+ // Call useUI to trigger the config merging
154
+ useUI(defaultConfig);
155
+
156
+ expect(uiUtils.getMergedConfig).toHaveBeenCalledWith({
157
+ defaultConfig,
158
+ globalConfig,
159
+ propsConfig,
160
+ unstyled: false,
161
+ });
162
+ });
163
+ });
164
+
165
+ describe("CVA Integration", () => {
166
+ it("should generate classes using CVA when config has variants", () => {
167
+ const defaultConfig = {
168
+ body: {
169
+ base: "base-class",
170
+ variants: {
171
+ variant: {
172
+ primary: "primary-class",
173
+ secondary: "secondary-class",
174
+ },
175
+ },
176
+ },
177
+ };
178
+
179
+ // Call useUI to trigger CVA usage
180
+ useUI(defaultConfig);
181
+
182
+ expect(uiUtils.cva).toHaveBeenCalledWith(defaultConfig.body);
183
+ });
184
+
185
+ it("should handle string-based config values", () => {
186
+ const defaultConfig = {
187
+ body: "simple-string-class",
188
+ };
189
+
190
+ const result = useUI(defaultConfig);
191
+ const bodyAttrs = result.bodyAttrs;
192
+
193
+ expect(bodyAttrs.value.class).toContain("simple-string-class");
194
+ });
195
+ });
196
+
197
+ describe("Color Handling", () => {
198
+ it("should replace {color} placeholders in classes", () => {
199
+ // Mock getCurrentInstance to return color prop
200
+ vi.mocked(getCurrentInstance).mockReturnValue({
201
+ type: { __name: "TestComponent" },
202
+ props: { color: "blue", dataTest: "test" },
203
+ parent: null,
204
+ });
205
+
206
+ // Test the setColor function directly
207
+ expect(uiUtils.setColor).toBeDefined();
208
+
209
+ // The setColor function should replace {color} placeholders
210
+ const testClasses = "text-{color} bg-{color}";
211
+ const coloredClasses = uiUtils.setColor(testClasses, "blue");
212
+
213
+ expect(coloredClasses).toBe("text-blue bg-blue");
214
+
215
+ // Test that useUI can be called with color-containing config
216
+ const defaultConfig = {
217
+ wrapper: {
218
+ base: "text-{color} bg-{color}",
219
+ },
220
+ };
221
+
222
+ const result = useUI(defaultConfig);
223
+
224
+ expect(result).toHaveProperty("config");
225
+ expect(result).toHaveProperty("getDataTest");
226
+ });
227
+ });
228
+
229
+ describe("getDataTest Function", () => {
230
+ it("should return data-test value when dataTest prop is provided", () => {
231
+ const result = useUI({});
232
+ const dataTest = result.getDataTest();
233
+
234
+ expect(dataTest).toBe("test");
235
+ });
236
+
237
+ it("should return data-test with suffix when suffix is provided", () => {
238
+ const result = useUI({});
239
+ const dataTest = result.getDataTest("button");
240
+
241
+ expect(dataTest).toBe("test-button");
242
+ });
243
+
244
+ it("should return null when dataTest prop is not provided", () => {
245
+ // Mock getCurrentInstance to return no dataTest
246
+ vi.mocked(getCurrentInstance).mockReturnValue({
247
+ type: { __name: "TestComponent" },
248
+ props: {},
249
+ parent: null,
250
+ });
251
+
252
+ const result = useUI({});
253
+ const dataTest = result.getDataTest();
254
+
255
+ expect(dataTest).toBeNull();
256
+ });
257
+ });
258
+
259
+ describe("Nested Component Handling", () => {
260
+ it("should handle nested component references like {UIcon}", () => {
261
+ const defaultConfig = {
262
+ icon: "{UIcon}",
263
+ button: "btn {UIcon} end",
264
+ };
265
+
266
+ const result = useUI(defaultConfig);
267
+
268
+ // The nested component pattern should be processed
269
+ expect(result).toHaveProperty("iconAttrs");
270
+ expect(result).toHaveProperty("buttonAttrs");
271
+ });
272
+ });
273
+
274
+ describe("Reactivity", () => {
275
+ it("should update config when props.config changes", async () => {
276
+ const component = mount(TestComponent);
277
+
278
+ // Initial state
279
+ expect(component.vm.config).toBeDefined();
280
+
281
+ // Change props
282
+ await component.setProps({
283
+ config: {
284
+ body: { base: "new-class" },
285
+ },
286
+ });
287
+
288
+ await nextTick();
289
+
290
+ // Config should be updated
291
+ expect(uiUtils.getMergedConfig).toHaveBeenCalled();
292
+ });
293
+ });
294
+
295
+ describe("Extends Pattern Handling", () => {
296
+ it("should handle extends pattern {>key} syntax", () => {
297
+ const defaultConfig = {
298
+ button: "base-class {>icon}",
299
+ icon: "icon-class",
300
+ };
301
+
302
+ const result = useUI(defaultConfig);
303
+
304
+ expect(result).toHaveProperty("buttonAttrs");
305
+ expect(result).toHaveProperty("iconAttrs");
306
+ });
307
+ });
308
+
309
+ describe("Mutated Props", () => {
310
+ it("should use mutated props for class generation", () => {
311
+ const defaultConfig = {
312
+ body: {
313
+ base: "base-class",
314
+ variants: {
315
+ hasIcon: {
316
+ true: "with-icon",
317
+ false: "without-icon",
318
+ },
319
+ },
320
+ },
321
+ };
322
+
323
+ const mutatedProps = computed(() => ({
324
+ hasIcon: true,
325
+ }));
326
+
327
+ const result = useUI(defaultConfig, mutatedProps);
328
+
329
+ expect(result).toHaveProperty("bodyAttrs");
330
+ });
331
+ });
332
+
333
+ describe("Component Name Detection", () => {
334
+ it("should detect component name from type.__name", () => {
335
+ vi.mocked(getCurrentInstance).mockReturnValue({
336
+ type: { __name: "UButton" },
337
+ props: { dataTest: "test" },
338
+ parent: null,
339
+ });
340
+
341
+ const result = useUI({});
342
+
343
+ expect(result).toBeDefined();
344
+ });
345
+
346
+ it("should detect component name from parent when internal component", () => {
347
+ vi.mocked(getCurrentInstance).mockReturnValue({
348
+ type: { internal: true },
349
+ props: { dataTest: "test" },
350
+ parent: {
351
+ type: { __name: "UButton" },
352
+ },
353
+ });
354
+
355
+ const result = useUI({});
356
+
357
+ expect(result).toBeDefined();
358
+ });
359
+ });
360
+
361
+ describe("Attribute Generation", () => {
362
+ it("should generate proper attributes for each config key", () => {
363
+ const defaultConfig = {
364
+ wrapper: { base: "wrapper-class" },
365
+ content: { base: "content-class" },
366
+ footer: "footer-class",
367
+ };
368
+
369
+ const result = useUI(defaultConfig);
370
+
371
+ expect(result).toHaveProperty("wrapperAttrs");
372
+ expect(result).toHaveProperty("contentAttrs");
373
+ expect(result).toHaveProperty("footerAttrs");
374
+ });
375
+
376
+ it("should include config in attributes for nested components", () => {
377
+ const defaultConfig = {
378
+ icon: {
379
+ base: "{UIcon}",
380
+ defaults: {
381
+ size: "sm",
382
+ },
383
+ },
384
+ };
385
+
386
+ const result = useUI(defaultConfig);
387
+ const iconAttrs = result.iconAttrs;
388
+
389
+ expect(iconAttrs.value).toHaveProperty("config");
390
+ });
391
+ });
392
+
393
+ describe("Edge Cases", () => {
394
+ it("should handle empty config", () => {
395
+ const result = useUI({});
396
+
397
+ expect(result.config).toBeDefined();
398
+ expect(result.getDataTest).toBeDefined();
399
+ });
400
+
401
+ it("should handle null/undefined config values", () => {
402
+ const defaultConfig = {
403
+ body: "",
404
+ icon: undefined,
405
+ };
406
+
407
+ const result = useUI(defaultConfig);
408
+
409
+ expect(result).toHaveProperty("bodyAttrs");
410
+ expect(result).toHaveProperty("iconAttrs");
411
+
412
+ // Should not throw errors when accessing attributes
413
+ expect(() => result.bodyAttrs.value).not.toThrow();
414
+ expect(() => result.iconAttrs.value).not.toThrow();
415
+ });
416
+
417
+ it("should handle unstyled mode", () => {
418
+ uiUtils.vuelessConfig.unstyled = true;
419
+
420
+ const defaultConfig = {
421
+ body: { base: "styled-class" },
422
+ };
423
+
424
+ useUI(defaultConfig);
425
+
426
+ expect(uiUtils.getMergedConfig).toHaveBeenCalledWith(
427
+ expect.objectContaining({
428
+ unstyled: true,
429
+ }),
430
+ );
431
+ });
432
+
433
+ it("should handle config with only defaults", () => {
434
+ const defaultConfig = {
435
+ defaults: {
436
+ variant: "primary",
437
+ size: "md",
438
+ },
439
+ };
440
+
441
+ const result = useUI(defaultConfig);
442
+
443
+ expect(result.config).toBeDefined();
444
+ });
445
+
446
+ it("should handle deep config changes", async () => {
447
+ const component = mount(TestComponent);
448
+
449
+ // Change nested config
450
+ await component.setProps({
451
+ config: {
452
+ body: {
453
+ base: "new-base",
454
+ variants: {
455
+ variant: {
456
+ custom: "custom-class",
457
+ },
458
+ },
459
+ },
460
+ },
461
+ });
462
+
463
+ await nextTick();
464
+
465
+ expect(uiUtils.getMergedConfig).toHaveBeenCalled();
466
+ });
467
+ });
468
+
469
+ describe("Integration Tests", () => {
470
+ it("should work with real component mounting", () => {
471
+ const component = mount(TestComponent);
472
+
473
+ expect(component.exists()).toBe(true);
474
+ expect(component.attributes("data-test")).toBe("test");
475
+ });
476
+
477
+ it("should handle prop changes in mounted component", async () => {
478
+ const component = mount(TestComponent);
479
+
480
+ // The component should render with the default dataTest from the mock
481
+ expect(component.exists()).toBe(true);
482
+
483
+ // Test that the component can handle config changes
484
+ await component.setProps({
485
+ config: {
486
+ body: { base: "new-config-class" },
487
+ },
488
+ });
489
+
490
+ // The component should still exist and function after prop changes
491
+ expect(component.exists()).toBe(true);
492
+ });
493
+ });
494
+ });