banhaten 0.1.2 → 0.2.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 (365) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/CONTRIBUTING.md +44 -0
  3. package/README.md +145 -328
  4. package/SECURITY.md +15 -0
  5. package/banhaten.config.example.json +1 -3
  6. package/docs/design-system/README.md +20 -0
  7. package/docs/design-system/adoption-quality-remediation.md +87 -0
  8. package/docs/design-system/agent-usage.md +13 -0
  9. package/docs/design-system/appearance-presets.md +189 -0
  10. package/docs/design-system/appearances/default.md +94 -0
  11. package/docs/design-system/appearances/rounded.md +95 -0
  12. package/docs/design-system/appearances/sharp.md +95 -0
  13. package/docs/design-system/component-docs-pattern.md +81 -0
  14. package/docs/design-system/component-showcase-consistency-report.md +217 -0
  15. package/docs/design-system/component-token-consistency-audit.md +173 -0
  16. package/docs/design-system/components/README.md +75 -0
  17. package/docs/design-system/components/accordion.md +67 -0
  18. package/docs/design-system/components/activity-feed.md +106 -0
  19. package/docs/design-system/components/alert-dialog.md +87 -0
  20. package/docs/design-system/components/alert.md +111 -0
  21. package/docs/design-system/components/aspect-ratio.md +59 -0
  22. package/docs/design-system/components/attribute.md +103 -0
  23. package/docs/design-system/components/autocomplete.md +93 -0
  24. package/docs/design-system/components/avatar.md +70 -0
  25. package/docs/design-system/components/badge.md +72 -0
  26. package/docs/design-system/components/banner.md +99 -0
  27. package/docs/design-system/components/breadcrumbs.md +188 -0
  28. package/docs/design-system/components/button-group.md +117 -0
  29. package/docs/design-system/components/button.md +95 -0
  30. package/docs/design-system/components/card.md +95 -0
  31. package/docs/design-system/components/carousel.md +61 -0
  32. package/docs/design-system/components/catalog-components.md +59 -0
  33. package/docs/design-system/components/chart.md +58 -0
  34. package/docs/design-system/components/checkbox.md +68 -0
  35. package/docs/design-system/components/collapsible.md +63 -0
  36. package/docs/design-system/components/command.md +75 -0
  37. package/docs/design-system/components/context-menu.md +59 -0
  38. package/docs/design-system/components/date-picker.md +97 -0
  39. package/docs/design-system/components/divider.md +117 -0
  40. package/docs/design-system/components/empty-state.md +72 -0
  41. package/docs/design-system/components/field.md +86 -0
  42. package/docs/design-system/components/file-upload.md +199 -0
  43. package/docs/design-system/components/hover-card.md +61 -0
  44. package/docs/design-system/components/icons.md +48 -0
  45. package/docs/design-system/components/input-group.md +73 -0
  46. package/docs/design-system/components/input-otp.md +73 -0
  47. package/docs/design-system/components/input.md +84 -0
  48. package/docs/design-system/components/item.md +72 -0
  49. package/docs/design-system/components/kbd.md +62 -0
  50. package/docs/design-system/components/label.md +64 -0
  51. package/docs/design-system/components/menu.md +76 -0
  52. package/docs/design-system/components/menubar.md +60 -0
  53. package/docs/design-system/components/modal.md +119 -0
  54. package/docs/design-system/components/native-select.md +70 -0
  55. package/docs/design-system/components/navigation-menu.md +63 -0
  56. package/docs/design-system/components/onboarding-step-list-item.md +94 -0
  57. package/docs/design-system/components/page-header.md +98 -0
  58. package/docs/design-system/components/pagination.md +71 -0
  59. package/docs/design-system/components/popover.md +77 -0
  60. package/docs/design-system/components/progress-slider.md +66 -0
  61. package/docs/design-system/components/progress.md +92 -0
  62. package/docs/design-system/components/radio-card.md +65 -0
  63. package/docs/design-system/components/radio-group.md +72 -0
  64. package/docs/design-system/components/resizable.md +57 -0
  65. package/docs/design-system/components/scroll-area.md +60 -0
  66. package/docs/design-system/components/segmented-control.md +71 -0
  67. package/docs/design-system/components/select.md +87 -0
  68. package/docs/design-system/components/sheet.md +82 -0
  69. package/docs/design-system/components/sidebar.md +83 -0
  70. package/docs/design-system/components/skeleton.md +91 -0
  71. package/docs/design-system/components/slideout.md +77 -0
  72. package/docs/design-system/components/slider.md +81 -0
  73. package/docs/design-system/components/social-button.md +65 -0
  74. package/docs/design-system/components/spinner.md +77 -0
  75. package/docs/design-system/components/steps.md +77 -0
  76. package/docs/design-system/components/table.md +411 -0
  77. package/docs/design-system/components/tabs.md +69 -0
  78. package/docs/design-system/components/tag.md +95 -0
  79. package/docs/design-system/components/textarea.md +64 -0
  80. package/docs/design-system/components/timeline.md +95 -0
  81. package/docs/design-system/components/toast.md +73 -0
  82. package/docs/design-system/components/toggle.md +96 -0
  83. package/docs/design-system/components/toolbar.md +105 -0
  84. package/docs/design-system/components/tooltip.md +107 -0
  85. package/docs/design-system/components/typography.md +18 -0
  86. package/docs/design-system/density-and-elevation.md +44 -0
  87. package/docs/design-system/design-system-test-missing-items.md +368 -0
  88. package/docs/design-system/direction-and-localization.md +23 -0
  89. package/docs/design-system/form-recipes.md +73 -0
  90. package/docs/design-system/gap-promotion-policy.md +21 -0
  91. package/docs/design-system/gap-promotion-report.md +166 -0
  92. package/docs/design-system/icons.md +69 -0
  93. package/docs/design-system/installation.md +68 -0
  94. package/docs/design-system/launch-comparison-shadcn.md +98 -0
  95. package/docs/design-system/registry-and-cli.md +64 -0
  96. package/docs/design-system/release-policy.md +30 -0
  97. package/docs/design-system/roadmap.md +14 -0
  98. package/docs/design-system/support-matrix.md +23 -0
  99. package/docs/design-system/tabs.md +53 -0
  100. package/docs/design-system/token-governance.md +60 -0
  101. package/package.json +97 -65
  102. package/registry/component-contract.json +40 -0
  103. package/registry/components/alert-dialog.tsx +297 -0
  104. package/registry/components/alert.tsx +86 -65
  105. package/registry/components/aspect-ratio.tsx +30 -0
  106. package/registry/components/autocomplete.tsx +23 -9
  107. package/registry/components/avatar.tsx +7 -7
  108. package/registry/components/badge.tsx +1 -1
  109. package/registry/components/button-group.tsx +321 -4
  110. package/registry/components/button.tsx +38 -17
  111. package/registry/components/card.tsx +68 -59
  112. package/registry/components/carousel.tsx +282 -0
  113. package/registry/components/chart.tsx +203 -0
  114. package/registry/components/checkbox.tsx +3 -3
  115. package/registry/components/collapsible.tsx +69 -0
  116. package/registry/components/command.tsx +174 -0
  117. package/registry/components/context-menu.tsx +236 -0
  118. package/registry/components/date-picker.tsx +246 -105
  119. package/registry/components/expanded/ActivityFeed.tsx +6 -15
  120. package/registry/components/expanded/Banner.tsx +3 -3
  121. package/registry/components/expanded/Breadcrumbs.tsx +5 -5
  122. package/registry/components/expanded/CatalogComponentsShowcase.tsx +13 -13
  123. package/registry/components/expanded/CatalogDivider.tsx +1 -1
  124. package/registry/components/expanded/EmptyState.tsx +1 -1
  125. package/registry/components/expanded/FileUpload.tsx +9 -2
  126. package/registry/components/expanded/OnboardingStepListItem.tsx +2 -4
  127. package/registry/components/expanded/PageHeader.tsx +14 -14
  128. package/registry/components/expanded/Slideout.tsx +15 -415
  129. package/registry/components/expanded/Steps.tsx +4 -4
  130. package/registry/components/expanded/Table.tsx +116 -69
  131. package/registry/components/expanded/Timeline.tsx +3 -3
  132. package/registry/components/expanded/activityFeed.css +1 -1
  133. package/registry/components/expanded/breadcrumbs.css +139 -139
  134. package/registry/components/expanded/catalogComponentsShowcase.css +83 -83
  135. package/registry/components/expanded/divider.css +1 -1
  136. package/registry/components/expanded/emptyState.css +1 -1
  137. package/registry/components/expanded/fileUpload.css +6 -6
  138. package/registry/components/expanded/slideout.css +9 -685
  139. package/registry/components/expanded/steps.css +274 -274
  140. package/registry/components/expanded/table.css +2 -2
  141. package/registry/components/expanded/timeline.css +264 -264
  142. package/registry/components/field.tsx +230 -0
  143. package/registry/components/hover-card.tsx +48 -0
  144. package/registry/components/input-content.ts +1 -0
  145. package/registry/components/input-group.tsx +130 -0
  146. package/registry/components/input-otp.tsx +13 -3
  147. package/registry/components/input.tsx +70 -45
  148. package/registry/components/item.tsx +189 -0
  149. package/registry/components/kbd.tsx +151 -0
  150. package/registry/components/label.tsx +78 -0
  151. package/registry/components/menu.tsx +34 -246
  152. package/registry/components/menubar.tsx +226 -0
  153. package/registry/components/modal.tsx +216 -143
  154. package/registry/components/native-select.tsx +205 -0
  155. package/registry/components/navigation-menu.tsx +171 -0
  156. package/registry/components/pagination.tsx +80 -58
  157. package/registry/components/popover.tsx +162 -664
  158. package/registry/components/radio-card.tsx +116 -87
  159. package/registry/components/radio-group.tsx +1 -1
  160. package/registry/components/resizable.tsx +74 -0
  161. package/registry/components/scroll-area.tsx +67 -0
  162. package/registry/components/segmented-control.tsx +87 -0
  163. package/registry/components/select-content.tsx +2 -2
  164. package/registry/components/select.tsx +54 -20
  165. package/registry/components/sheet.tsx +305 -0
  166. package/registry/components/sidebar.tsx +352 -0
  167. package/registry/components/skeleton.css +0 -3
  168. package/registry/components/social-button.tsx +116 -30
  169. package/registry/components/spinner.tsx +18 -55
  170. package/registry/components/{expanded/tabs.css → tabs.css} +127 -106
  171. package/registry/components/tabs.tsx +246 -0
  172. package/registry/components/tag.tsx +2 -2
  173. package/registry/components/textarea.tsx +4 -4
  174. package/registry/components/toast.tsx +136 -0
  175. package/registry/components/toggle.tsx +2 -2
  176. package/registry/components/toolbar.tsx +89 -17
  177. package/registry/components/tooltip.tsx +1 -1
  178. package/registry/examples/alert-demo.tsx +25 -5
  179. package/registry/examples/alert-dialog-demo.tsx +42 -0
  180. package/registry/examples/aspect-ratio-demo.tsx +11 -0
  181. package/registry/examples/attribute-demo.tsx +1 -1
  182. package/registry/examples/autocomplete-demo.tsx +3 -3
  183. package/registry/examples/avatar-demo.tsx +7 -7
  184. package/registry/examples/badge-demo.tsx +1 -1
  185. package/registry/examples/button-demo.tsx +6 -6
  186. package/registry/examples/button-group-demo.tsx +23 -2
  187. package/registry/examples/card-demo.tsx +46 -8
  188. package/registry/examples/carousel-demo.tsx +43 -0
  189. package/registry/examples/chart-demo.tsx +33 -0
  190. package/registry/examples/checkbox-demo.tsx +5 -5
  191. package/registry/examples/collapsible-demo.tsx +16 -0
  192. package/registry/examples/collection-filter-bar-demo.tsx +135 -0
  193. package/registry/examples/command-demo.tsx +42 -0
  194. package/registry/examples/{expanded/command-bar-demo.tsx → command-palette-demo.tsx} +5 -15
  195. package/registry/examples/context-menu-demo.tsx +29 -0
  196. package/registry/examples/control-density-contract-demo.tsx +61 -0
  197. package/registry/examples/date-picker-demo.tsx +12 -12
  198. package/registry/examples/divider-demo.tsx +3 -3
  199. package/registry/examples/expanded/banner-demo.tsx +1 -1
  200. package/registry/examples/expanded/breadcrumbs-demo.tsx +1 -1
  201. package/registry/examples/expanded/empty-state-demo.tsx +1 -1
  202. package/registry/examples/expanded/file-upload-demo.tsx +1 -1
  203. package/registry/examples/expanded/onboarding-step-demo.tsx +1 -1
  204. package/registry/examples/expanded/slideout-demo.tsx +2 -2
  205. package/registry/examples/expanded/steps-demo.tsx +1 -1
  206. package/registry/examples/expanded/tabs-demo.tsx +2 -2
  207. package/registry/examples/expanded/timeline-demo.tsx +1 -1
  208. package/registry/examples/field-demo.tsx +51 -0
  209. package/registry/examples/hover-card-demo.tsx +23 -0
  210. package/registry/examples/input-demo.tsx +6 -6
  211. package/registry/examples/input-group-demo.tsx +16 -0
  212. package/registry/examples/input-otp-demo.tsx +4 -4
  213. package/registry/examples/item-demo.tsx +46 -0
  214. package/registry/examples/kbd-demo.tsx +11 -0
  215. package/registry/examples/label-demo.tsx +20 -0
  216. package/registry/examples/menu-demo.tsx +1 -1
  217. package/registry/examples/menubar-demo.tsx +34 -0
  218. package/registry/examples/modal-demo.tsx +67 -9
  219. package/registry/examples/native-select-demo.tsx +16 -0
  220. package/registry/examples/navigation-menu-demo.tsx +29 -0
  221. package/registry/examples/pagination-demo.tsx +6 -3
  222. package/registry/examples/popover-demo.tsx +19 -19
  223. package/registry/examples/progress-demo.tsx +1 -1
  224. package/registry/examples/progress-slider-demo.tsx +1 -1
  225. package/registry/examples/radio-card-demo.tsx +4 -4
  226. package/registry/examples/radio-group-demo.tsx +2 -2
  227. package/registry/examples/resizable-demo.tsx +22 -0
  228. package/registry/examples/results-toolbar-demo.tsx +44 -0
  229. package/registry/examples/scroll-area-demo.tsx +15 -0
  230. package/registry/examples/segmented-control-demo.tsx +39 -0
  231. package/registry/examples/select-demo.tsx +1 -1
  232. package/registry/examples/sheet-demo.tsx +47 -0
  233. package/registry/examples/sidebar-demo.tsx +55 -0
  234. package/registry/examples/slider-demo.tsx +3 -3
  235. package/registry/examples/social-button-demo.tsx +18 -8
  236. package/registry/examples/spinner-demo.tsx +13 -13
  237. package/registry/examples/tabs-demo.tsx +13 -0
  238. package/registry/examples/tag-demo.tsx +1 -1
  239. package/registry/examples/textarea-demo.tsx +4 -4
  240. package/registry/examples/toast-demo.tsx +35 -0
  241. package/registry/examples/toggle-demo.tsx +6 -6
  242. package/registry/examples/tooltip-demo.tsx +4 -4
  243. package/registry/guidance/USAGE.md +27 -0
  244. package/registry/hooks/use-direction.ts +193 -13
  245. package/registry/index.json +6010 -432
  246. package/registry/styles/globals.css +3112 -4690
  247. package/registry.json +1582 -0
  248. package/schema/config.schema.json +51 -0
  249. package/schema/registry.schema.json +190 -0
  250. package/schema/tokens.schema.json +163 -0
  251. package/src/cli/command-line.js +194 -0
  252. package/src/cli/config-editors.js +9 -0
  253. package/src/cli/doctor.js +399 -0
  254. package/src/cli/file-transaction.js +295 -0
  255. package/src/cli/global-css.js +153 -0
  256. package/src/cli/index.js +639 -1391
  257. package/src/cli/install-plan.js +592 -0
  258. package/src/cli/install-receipt.js +171 -0
  259. package/src/cli/jsonc.js +203 -0
  260. package/src/cli/package-install-state.js +254 -0
  261. package/src/cli/package-manager.js +238 -0
  262. package/src/cli/package-plan.js +185 -0
  263. package/src/cli/package-version.js +97 -0
  264. package/src/cli/registry-query.js +207 -0
  265. package/src/cli/reporting.js +55 -0
  266. package/src/cli/schema-validation.js +19 -0
  267. package/src/cli/source-scanner.js +686 -0
  268. package/src/cli/vite-config.js +478 -0
  269. package/tokens/banhaten.tokens.json +4313 -310
  270. package/registry/assets/avatars/avatar-02.jpg +0 -0
  271. package/registry/assets/avatars/avatar-03.jpg +0 -0
  272. package/registry/assets/avatars/avatar-04.jpg +0 -0
  273. package/registry/assets/avatars/avatar-05.jpg +0 -0
  274. package/registry/assets/avatars/avatar-06.jpg +0 -0
  275. package/registry/assets/avatars/avatar-07.jpg +0 -0
  276. package/registry/assets/avatars/avatar-08.jpg +0 -0
  277. package/registry/assets/avatars/avatar-09.jpg +0 -0
  278. package/registry/assets/avatars/avatar-10.jpg +0 -0
  279. package/registry/assets/avatars/avatar-11.jpg +0 -0
  280. package/registry/assets/avatars/avatar-12.jpg +0 -0
  281. package/registry/assets/avatars/avatar-13.jpg +0 -0
  282. package/registry/assets/avatars/avatar-14.jpg +0 -0
  283. package/registry/assets/avatars/avatar-15.jpg +0 -0
  284. package/registry/assets/avatars/avatar-16.jpg +0 -0
  285. package/registry/assets/avatars/avatar-17.jpg +0 -0
  286. package/registry/assets/avatars/avatar-18.jpg +0 -0
  287. package/registry/assets/avatars/avatar-19.jpg +0 -0
  288. package/registry/assets/avatars/avatar-20.jpg +0 -0
  289. package/registry/assets/avatars/avatar-21.jpg +0 -0
  290. package/registry/assets/avatars/avatar-22.jpg +0 -0
  291. package/registry/assets/avatars/avatar-23.jpg +0 -0
  292. package/registry/assets/avatars/avatar-24.jpg +0 -0
  293. package/registry/assets/avatars/avatar-25.jpg +0 -0
  294. package/registry/assets/avatars/avatar-26.jpg +0 -0
  295. package/registry/assets/avatars/avatar-27.jpg +0 -0
  296. package/registry/assets/avatars/avatar-28.jpg +0 -0
  297. package/registry/assets/avatars/avatar-29.jpg +0 -0
  298. package/registry/assets/avatars/avatar-30.jpg +0 -0
  299. package/registry/assets/avatars/avatar-31.jpg +0 -0
  300. package/registry/assets/avatars/avatar-32.jpg +0 -0
  301. package/registry/assets/avatars/avatar-33.jpg +0 -0
  302. package/registry/assets/avatars/avatar-34.jpg +0 -0
  303. package/registry/assets/avatars/avatar-35.jpg +0 -0
  304. package/registry/assets/image-assets.json +0 -744
  305. package/registry/assets/images/art-02.jpg +0 -0
  306. package/registry/assets/images/art-03.jpg +0 -0
  307. package/registry/assets/images/art-04.jpg +0 -0
  308. package/registry/assets/images/art-05.jpg +0 -0
  309. package/registry/assets/images/art-06.jpg +0 -0
  310. package/registry/assets/images/art-07.jpg +0 -0
  311. package/registry/assets/images/art-08.jpg +0 -0
  312. package/registry/assets/images/art-09.jpg +0 -0
  313. package/registry/assets/images/art-10.jpg +0 -0
  314. package/registry/assets/images/art-11.jpg +0 -0
  315. package/registry/assets/images/art-12.jpg +0 -0
  316. package/registry/assets/images/art-13.jpg +0 -0
  317. package/registry/assets/images/art-14.jpg +0 -0
  318. package/registry/assets/images/art-15.jpg +0 -0
  319. package/registry/assets/images/art-16.jpg +0 -0
  320. package/registry/assets/images/art-17.jpg +0 -0
  321. package/registry/assets/images/art-18.jpg +0 -0
  322. package/registry/assets/images/art-19.jpg +0 -0
  323. package/registry/assets/images/art-20.jpg +0 -0
  324. package/registry/assets/images/art-21.jpg +0 -0
  325. package/registry/assets/images/art-22.jpg +0 -0
  326. package/registry/assets/images/art-23.jpg +0 -0
  327. package/registry/assets/images/art-24.jpg +0 -0
  328. package/registry/assets/images/art-25.jpg +0 -0
  329. package/registry/assets/images/art-26.jpg +0 -0
  330. package/registry/assets/images/art-27.jpg +0 -0
  331. package/registry/assets/images/nature-01.jpg +0 -0
  332. package/registry/assets/images/nature-02.jpg +0 -0
  333. package/registry/assets/images/nature-03.jpg +0 -0
  334. package/registry/assets/images/nature-04.jpg +0 -0
  335. package/registry/assets/images/nature-05.jpg +0 -0
  336. package/registry/assets/images/nature-06.jpg +0 -0
  337. package/registry/assets/images/nature-07.jpg +0 -0
  338. package/registry/assets/images/nature-08.jpg +0 -0
  339. package/registry/assets/images/nature-09.jpg +0 -0
  340. package/registry/assets/images/nature-10.jpg +0 -0
  341. package/registry/assets/images/nature-11.jpg +0 -0
  342. package/registry/assets/images/nature-12.jpg +0 -0
  343. package/registry/assets/images/nature-13.jpg +0 -0
  344. package/registry/assets/images/nature-14.jpg +0 -0
  345. package/registry/assets/images/nature-15.jpg +0 -0
  346. package/registry/assets/images/nature-16.jpg +0 -0
  347. package/registry/assets/images/nature-17.jpg +0 -0
  348. package/registry/assets/images/nature-18.jpg +0 -0
  349. package/registry/assets/images/nature-19.jpg +0 -0
  350. package/registry/assets/images/nature-20.jpg +0 -0
  351. package/registry/components/expanded/BannerBoard.tsx +0 -225
  352. package/registry/components/expanded/CatalogTag.tsx +0 -85
  353. package/registry/components/expanded/CommandBar.tsx +0 -386
  354. package/registry/components/expanded/IconExplorer.tsx +0 -612
  355. package/registry/components/expanded/Tabs.tsx +0 -86
  356. package/registry/components/expanded/TimelineShowcase.tsx +0 -158
  357. package/registry/components/expanded/commandBar.css +0 -470
  358. package/registry/components/expanded/iconExplorer.css +0 -764
  359. package/registry/components/expanded/iconPacks.ts +0 -866
  360. package/registry/components/expanded/tag.css +0 -148
  361. package/registry/examples/expanded/icons-demo.tsx +0 -5
  362. package/tokens/Color mode.zip +0 -0
  363. package/tokens/Numbers.zip +0 -0
  364. package/tokens/Radius.zip +0 -0
  365. package/tokens/Theme.zip +0 -0
@@ -0,0 +1,478 @@
1
+ // The scanner reports syntax problems without naming a dialect; this module is
2
+ // the only place that knows the source is a Vite config, so it adds that word.
3
+ function asViteConfigError(error) {
4
+ if (error instanceof Error && !/^unsupported Vite config: /.test(error.message)) {
5
+ error.message = `unsupported Vite config: ${error.message}`
6
+ }
7
+ return error
8
+ }
9
+
10
+ // Vite config editing. This module owns every assumption about what a Vite
11
+ // config looks like; the scanner primitives it builds on know nothing about Vite.
12
+
13
+ import {
14
+ appendArrayElement,
15
+ appendObjectProperty,
16
+ arrayHasCallExpression,
17
+ createBracketRange,
18
+ expectArrayLiteralValue,
19
+ expectObjectLiteralValue,
20
+ findExportDefaultExpressionStart,
21
+ findObjectProperty,
22
+ findTopLevelCommaOrEnd,
23
+ insertObjectProperty,
24
+ parseImportDeclarations,
25
+ prependImportLines,
26
+ readIdentifierAt,
27
+ readObjectPropertyNames,
28
+ readPropertyName,
29
+ readStringLiteral,
30
+ skipWhitespaceAndComments,
31
+ skipWhitespaceCommentsAndCommas
32
+ } from "./source-scanner.js"
33
+
34
+ function ensureNodeUrlImport(source) {
35
+ const imports = parseImportDeclarations(source)
36
+ const nodeUrlImports = imports.filter((entry) => entry.moduleSpecifier === "node:url")
37
+ const importedLocals = new Set(nodeUrlImports.flatMap((entry) => entry.namedLocals))
38
+ const missing = ["fileURLToPath", "URL"].filter((name) => !importedLocals.has(name))
39
+
40
+ if (missing.length === 0) return source
41
+
42
+ return prependImportLines(
43
+ source,
44
+ [`import { ${missing.join(", ")} } from "node:url"`]
45
+ )
46
+ }
47
+
48
+ function ensureTailwindViteImport(source, localName) {
49
+ const imports = parseImportDeclarations(source)
50
+ const tailwindImport = imports.find(
51
+ (entry) => entry.moduleSpecifier === "@tailwindcss/vite"
52
+ )
53
+
54
+ if (!tailwindImport) {
55
+ return prependImportLines(
56
+ source,
57
+ [`import ${localName} from "@tailwindcss/vite"`]
58
+ )
59
+ }
60
+
61
+ if (!tailwindImport.defaultLocal) {
62
+ throw new Error(
63
+ "unsupported Vite config: @tailwindcss/vite must be imported with a default binding."
64
+ )
65
+ }
66
+
67
+ return source
68
+ }
69
+
70
+ export function updateViteConfigSource(source, options) {
71
+ try {
72
+ return updateViteConfigSourceUnchecked(source, options)
73
+ } catch (error) {
74
+ throw asViteConfigError(error)
75
+ }
76
+ }
77
+
78
+ function updateViteConfigSourceUnchecked(source, { aliasRoot, sourceRoot }) {
79
+ validateViteConfigStructure(source)
80
+ const tailwindLocalName = getTailwindViteLocalName(source)
81
+ let next = aliasRoot
82
+ ? addViteResolveAlias(source, aliasRoot, sourceRoot)
83
+ : source
84
+ const addedFileUrlAlias = Boolean(aliasRoot) && next !== source
85
+ next = addVitePluginCall(next, `${tailwindLocalName}()`, tailwindLocalName)
86
+ next = ensureTailwindViteImport(next, tailwindLocalName)
87
+ if (addedFileUrlAlias) next = ensureNodeUrlImport(next)
88
+ return next
89
+ }
90
+
91
+ export function readViteAliasSourceRoot(source, options) {
92
+ try {
93
+ return readViteAliasSourceRootUnchecked(source, options)
94
+ } catch (error) {
95
+ throw asViteConfigError(error)
96
+ }
97
+ }
98
+
99
+ function readViteAliasSourceRootUnchecked(source, { aliasRoot }) {
100
+ validateViteConfigStructure(source)
101
+ const configObject = findViteConfigObject(source)
102
+ const resolveProperty = findObjectProperty(source, configObject, "resolve")
103
+ if (!resolveProperty) return null
104
+
105
+ const resolveObject = expectObjectLiteralValue(
106
+ source,
107
+ resolveProperty,
108
+ "unsupported Vite config: resolve must be an object literal."
109
+ )
110
+ const aliasProperty = findObjectProperty(source, resolveObject, "alias")
111
+ if (!aliasProperty) return null
112
+
113
+ const aliasObject = expectObjectLiteralValue(
114
+ source,
115
+ aliasProperty,
116
+ "unsupported Vite config: resolve.alias must be an object literal."
117
+ )
118
+ const rootProperty = findObjectProperty(source, aliasObject, aliasRoot)
119
+ if (!rootProperty) return null
120
+
121
+ const expression = source
122
+ .slice(rootProperty.valueStart, rootProperty.valueEnd)
123
+ .trim()
124
+ const staticRoot = readStaticAliasRoot(expression)
125
+ if (staticRoot !== null) {
126
+ assertStaticAliasBinding(source, staticRoot)
127
+ return staticRoot
128
+ }
129
+
130
+ throw new Error(
131
+ `unsupported Vite config: resolve.alias[${JSON.stringify(aliasRoot)}] must use a static string, ` +
132
+ "fileURLToPath(new URL(..., import.meta.url)), or path.resolve(__dirname, ...)."
133
+ )
134
+ }
135
+
136
+ export function readViteAliasKeys(source) {
137
+ try {
138
+ return readViteAliasKeysUnchecked(source)
139
+ } catch (error) {
140
+ throw asViteConfigError(error)
141
+ }
142
+ }
143
+
144
+ function readViteAliasKeysUnchecked(source) {
145
+ validateViteConfigStructure(source)
146
+ const configObject = findViteConfigObject(source)
147
+ const resolveProperty = findObjectProperty(source, configObject, "resolve")
148
+ if (!resolveProperty) return []
149
+
150
+ const resolveObject = expectObjectLiteralValue(
151
+ source,
152
+ resolveProperty,
153
+ "unsupported Vite config: resolve must be an object literal."
154
+ )
155
+ const aliasProperty = findObjectProperty(source, resolveObject, "alias")
156
+ if (!aliasProperty) return []
157
+
158
+ const aliasObject = expectObjectLiteralValue(
159
+ source,
160
+ aliasProperty,
161
+ "unsupported Vite config: resolve.alias must be an object literal."
162
+ )
163
+ return readObjectPropertyNames(source, aliasObject)
164
+ }
165
+
166
+ function readStaticAliasRoot(expression) {
167
+ const directLiteral = readStringLiteral(expression, 0)
168
+ if (
169
+ directLiteral &&
170
+ skipWhitespaceAndComments(expression, directLiteral.end) === expression.length
171
+ ) {
172
+ return { kind: "literal", value: directLiteral.value }
173
+ }
174
+
175
+ const patterns = [
176
+ {
177
+ kind: "file-url",
178
+ pattern: /^fileURLToPath\s*\(\s*new\s+URL\s*\(\s*(["'])(.*?)\1\s*,\s*import\.meta\.url\s*\)\s*\)$/s,
179
+ },
180
+ {
181
+ kind: "path-default",
182
+ pattern: /^path\.resolve\s*\(\s*(?:__dirname|import\.meta\.dirname)\s*,\s*(["'])(.*?)\1\s*\)$/s,
183
+ },
184
+ {
185
+ kind: "path-named",
186
+ pattern: /^resolve\s*\(\s*(?:__dirname|import\.meta\.dirname)\s*,\s*(["'])(.*?)\1\s*\)$/s,
187
+ },
188
+ ]
189
+
190
+ for (const candidate of patterns) {
191
+ const match = expression.match(candidate.pattern)
192
+ if (match) {
193
+ return {
194
+ kind: candidate.kind,
195
+ value: match[2].replace(/\\(.)/g, "$1"),
196
+ }
197
+ }
198
+ }
199
+
200
+ return null
201
+ }
202
+
203
+ function assertStaticAliasBinding(source, staticRoot) {
204
+ const imports = parseImportDeclarations(source)
205
+
206
+ if (staticRoot.kind === "file-url") {
207
+ const nodeUrlImport = imports.find(
208
+ (entry) =>
209
+ (entry.moduleSpecifier === "node:url" || entry.moduleSpecifier === "url") &&
210
+ entry.namedLocals.includes("fileURLToPath")
211
+ )
212
+ if (!nodeUrlImport) {
213
+ throw new Error(
214
+ "unsupported Vite config: fileURLToPath must be imported by name from node:url."
215
+ )
216
+ }
217
+ }
218
+
219
+ if (staticRoot.kind === "path-default") {
220
+ const nodePathImport = imports.find(
221
+ (entry) =>
222
+ (entry.moduleSpecifier === "node:path" || entry.moduleSpecifier === "path") &&
223
+ entry.defaultLocal === "path"
224
+ )
225
+ if (!nodePathImport) {
226
+ throw new Error(
227
+ "unsupported Vite config: path must be a default import from node:path."
228
+ )
229
+ }
230
+ }
231
+
232
+ if (staticRoot.kind === "path-named") {
233
+ const nodePathImport = imports.find(
234
+ (entry) =>
235
+ (entry.moduleSpecifier === "node:path" || entry.moduleSpecifier === "path") &&
236
+ entry.namedLocals.includes("resolve")
237
+ )
238
+ if (!nodePathImport) {
239
+ throw new Error(
240
+ "unsupported Vite config: resolve must be imported by name from node:path."
241
+ )
242
+ }
243
+ }
244
+ }
245
+
246
+ function validateViteConfigStructure(source) {
247
+ const configObject = findViteConfigObject(source)
248
+ assertStaticObjectStructure(source, configObject, "config")
249
+
250
+ const resolveProperty = findObjectProperty(source, configObject, "resolve")
251
+ if (!resolveProperty) return
252
+
253
+ const resolveObject = expectObjectLiteralValue(
254
+ source,
255
+ resolveProperty,
256
+ "unsupported Vite config: resolve must be an object literal."
257
+ )
258
+ assertStaticObjectStructure(source, resolveObject, "resolve")
259
+
260
+ const aliasProperty = findObjectProperty(source, resolveObject, "alias")
261
+ if (!aliasProperty) return
262
+
263
+ const aliasObject = expectObjectLiteralValue(
264
+ source,
265
+ aliasProperty,
266
+ "unsupported Vite config: resolve.alias must be an object literal."
267
+ )
268
+ assertStaticObjectStructure(source, aliasObject, "resolve.alias")
269
+ }
270
+
271
+ function assertStaticObjectStructure(source, objectRange, label) {
272
+ const bodyEnd = objectRange.end - 1
273
+ const seen = new Set()
274
+ let index = objectRange.start + 1
275
+
276
+ while (index < bodyEnd) {
277
+ index = skipWhitespaceCommentsAndCommas(source, index, bodyEnd)
278
+ if (index >= bodyEnd) return
279
+
280
+ const propertyStart = index
281
+ const propertyEnd = findTopLevelCommaOrEnd(
282
+ source,
283
+ propertyStart,
284
+ bodyEnd
285
+ )
286
+
287
+ if (source.startsWith("...", propertyStart)) {
288
+ throw new Error(
289
+ `unsupported Vite config: ${label} cannot contain spread properties.`
290
+ )
291
+ }
292
+ if (source[propertyStart] === "[") {
293
+ throw new Error(
294
+ `unsupported Vite config: ${label} cannot contain computed properties.`
295
+ )
296
+ }
297
+
298
+ const name = readPropertyName(source, propertyStart)
299
+ if (name) {
300
+ if (seen.has(name.name)) {
301
+ throw new Error(
302
+ `unsupported Vite config: ${label} contains duplicate property ${JSON.stringify(name.name)}.`
303
+ )
304
+ }
305
+ seen.add(name.name)
306
+ }
307
+
308
+ index = propertyEnd + 1
309
+ }
310
+ }
311
+
312
+ function getTailwindViteLocalName(source) {
313
+ const tailwindImport = parseImportDeclarations(source).find(
314
+ (entry) => entry.moduleSpecifier === "@tailwindcss/vite"
315
+ )
316
+
317
+ if (!tailwindImport) return "tailwindcss"
318
+ if (tailwindImport.defaultLocal) return tailwindImport.defaultLocal
319
+
320
+ throw new Error(
321
+ "unsupported Vite config: @tailwindcss/vite must be imported with a default binding."
322
+ )
323
+ }
324
+
325
+ function addViteResolveAlias(source, aliasRoot, sourceRoot) {
326
+ const configObject = findViteConfigObject(source)
327
+ const resolveProperty = findObjectProperty(source, configObject, "resolve")
328
+ const aliasEntry = `${JSON.stringify(aliasRoot)}: fileURLToPath(new URL(${JSON.stringify(
329
+ sourceRoot
330
+ )}, import.meta.url))`
331
+
332
+ if (!resolveProperty) {
333
+ return insertObjectProperty(
334
+ source,
335
+ configObject,
336
+ [
337
+ "resolve: {",
338
+ " alias: {",
339
+ ` ${aliasEntry},`,
340
+ " },",
341
+ "},",
342
+ ].join("\n")
343
+ )
344
+ }
345
+
346
+ const resolveObject = expectObjectLiteralValue(
347
+ source,
348
+ resolveProperty,
349
+ "unsupported Vite config: resolve must be an object literal."
350
+ )
351
+ const aliasProperty = findObjectProperty(source, resolveObject, "alias")
352
+
353
+ if (!aliasProperty) {
354
+ return insertObjectProperty(
355
+ source,
356
+ resolveObject,
357
+ [
358
+ "alias: {",
359
+ ` ${aliasEntry},`,
360
+ "},",
361
+ ].join("\n")
362
+ )
363
+ }
364
+
365
+ const aliasObject = expectObjectLiteralValue(
366
+ source,
367
+ aliasProperty,
368
+ "unsupported Vite config: resolve.alias must be an object literal."
369
+ )
370
+
371
+ if (findObjectProperty(source, aliasObject, aliasRoot)) {
372
+ return source
373
+ }
374
+
375
+ return appendObjectProperty(source, aliasObject, `${aliasEntry},`)
376
+ }
377
+
378
+ function addVitePluginCall(source, pluginCall, pluginLocalName) {
379
+ const configObject = findViteConfigObject(source)
380
+ const pluginsProperty = findObjectProperty(source, configObject, "plugins")
381
+
382
+ if (!pluginsProperty) {
383
+ return insertObjectProperty(source, configObject, `plugins: [${pluginCall}],`)
384
+ }
385
+
386
+ const pluginsArray = expectArrayLiteralValue(
387
+ source,
388
+ pluginsProperty,
389
+ "unsupported Vite config: plugins must be an array literal."
390
+ )
391
+
392
+ if (arrayHasCallExpression(source, pluginsArray, pluginLocalName)) {
393
+ return source
394
+ }
395
+
396
+ return appendArrayElement(source, pluginsArray, pluginCall)
397
+ }
398
+
399
+ function findViteConfigObject(source) {
400
+ const expressionStart = findExportDefaultExpressionStart(source)
401
+
402
+ if (expressionStart === -1) {
403
+ throw new Error(
404
+ "unsupported Vite config: expected `export default defineConfig({ ... })` or `export default { ... }`."
405
+ )
406
+ }
407
+
408
+ if (source[expressionStart] === "{") {
409
+ return createBracketRange(source, expressionStart, "{")
410
+ }
411
+
412
+ const callee = readIdentifierAt(source, expressionStart)
413
+ if (callee?.name !== "defineConfig") {
414
+ throw new Error(
415
+ "unsupported Vite config: default export must be `defineConfig({ ... })` or an object literal."
416
+ )
417
+ }
418
+
419
+ let index = skipWhitespaceAndComments(source, callee.end)
420
+ if (source[index] !== "(") {
421
+ throw new Error("unsupported Vite config: malformed defineConfig call.")
422
+ }
423
+
424
+ index = skipWhitespaceAndComments(source, index + 1)
425
+
426
+ // defineConfig({ ... })
427
+ if (source[index] === "{") {
428
+ return createBracketRange(source, index, "{")
429
+ }
430
+
431
+ // defineConfig((env) => ({ ... })) — the other documented Vite form, used
432
+ // whenever the config depends on `mode` or `command`. The config object is
433
+ // the arrow's returned expression, so resolve to that and edit it in place.
434
+ const arrowBody = findConfigArrowBodyStart(source, index)
435
+ if (arrowBody !== -1) {
436
+ return createBracketRange(source, arrowBody, "{")
437
+ }
438
+
439
+ throw new Error(
440
+ "unsupported Vite config: defineConfig must receive a config object literal " +
441
+ "or an arrow function returning one, e.g. `defineConfig(({ mode }) => ({ ... }))`."
442
+ )
443
+ }
444
+
445
+ // Given the index of defineConfig's first argument, return the index of the
446
+ // object literal an arrow function returns, or -1 when the argument is not an
447
+ // arrow whose body is a parenthesised object.
448
+ function findConfigArrowBodyStart(source, argumentStart) {
449
+ let index = argumentStart
450
+
451
+ // An optional `async` before the parameter list.
452
+ const asyncKeyword = readIdentifierAt(source, index)
453
+ if (asyncKeyword?.name === "async") {
454
+ index = skipWhitespaceAndComments(source, asyncKeyword.end)
455
+ }
456
+
457
+ // Parameter list: either `(...)` or a single bare identifier.
458
+ if (source[index] === "(") {
459
+ const parameters = createBracketRange(source, index, "(")
460
+ index = skipWhitespaceAndComments(source, parameters.end + 1)
461
+ } else {
462
+ const parameter = readIdentifierAt(source, index)
463
+ if (!parameter) return -1
464
+ index = skipWhitespaceAndComments(source, parameter.end)
465
+ }
466
+
467
+ if (source.slice(index, index + 2) !== "=>") return -1
468
+ index = skipWhitespaceAndComments(source, index + 2)
469
+
470
+ // `=> ({ ... })`. A block body (`=> { return { ... } }`) is deliberately not
471
+ // followed: locating the returned object there means tracking control flow,
472
+ // and editing it in place would be guesswork. Those users get the explicit
473
+ // message above rather than a silently wrong edit.
474
+ if (source[index] !== "(") return -1
475
+ index = skipWhitespaceAndComments(source, index + 1)
476
+
477
+ return source[index] === "{" ? index : -1
478
+ }