From 4be3a4bef9d6ea80e558caf0671c2513a4c73e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Tue, 2 Dec 2025 17:06:58 +0100 Subject: [PATCH 1/2] doc: use trusted registry URIs This writes each item to `/public/r/{name}.json` with contents, in order for the shadcn CLI to resolve those paths when using the trusted registry URI (`@diceui/{name}`). --- docs/content/docs/components/angle-slider.mdx | 2 +- docs/content/docs/components/avatar-group.mdx | 2 +- .../docs/components/badge-overflow.mdx | 2 +- .../docs/components/checkbox-group.mdx | 2 +- .../docs/components/circular-progress.mdx | 2 +- docs/content/docs/components/color-picker.mdx | 2 +- docs/content/docs/components/color-swatch.mdx | 2 +- docs/content/docs/components/combobox.mdx | 6 +- .../docs/components/compare-slider.mdx | 2 +- docs/content/docs/components/cropper.mdx | 2 +- docs/content/docs/components/editable.mdx | 2 +- docs/content/docs/components/file-upload.mdx | 2 +- docs/content/docs/components/fps.mdx | 2 +- docs/content/docs/components/kanban.mdx | 2 +- docs/content/docs/components/key-value.mdx | 2 +- docs/content/docs/components/listbox.mdx | 2 +- docs/content/docs/components/marquee.mdx | 4 +- docs/content/docs/components/mask-input.mdx | 2 +- docs/content/docs/components/masonry.mdx | 2 +- docs/content/docs/components/media-player.mdx | 2 +- docs/content/docs/components/mention.mdx | 2 +- docs/content/docs/components/qr-code.mdx | 2 +- docs/content/docs/components/rating.mdx | 2 +- .../docs/components/relative-time-card.mdx | 2 +- docs/content/docs/components/scroll-spy.mdx | 2 +- docs/content/docs/components/scroller.mdx | 2 +- .../docs/components/segmented-input.mdx | 2 +- docs/content/docs/components/sortable.mdx | 2 +- docs/content/docs/components/stack.mdx | 2 +- docs/content/docs/components/stat.mdx | 2 +- docs/content/docs/components/stepper.mdx | 2 +- docs/content/docs/components/tags-input.mdx | 2 +- docs/content/docs/components/time-picker.mdx | 2 +- docs/content/docs/components/timeline.mdx | 2 +- docs/content/docs/components/tour.mdx | 2 +- docs/content/docs/utilities/client-only.mdx | 2 +- docs/content/docs/utilities/composition.mdx | 2 +- .../docs/utilities/direction-provider.mdx | 2 +- docs/content/docs/utilities/hitbox.mdx | 2 +- docs/content/docs/utilities/portal.mdx | 2 +- docs/content/docs/utilities/presence.mdx | 2 +- .../docs/utilities/visually-hidden-input.mdx | 2 +- .../docs/utilities/visually-hidden.mdx | 2 +- docs/public/r/.gitignore | 1 + docs/scripts/build-registry.mts | 540 +++++++++--------- 45 files changed, 321 insertions(+), 312 deletions(-) create mode 100644 docs/public/r/.gitignore diff --git a/docs/content/docs/components/angle-slider.mdx b/docs/content/docs/components/angle-slider.mdx index b2cab8dc..6147c97a 100644 --- a/docs/content/docs/components/angle-slider.mdx +++ b/docs/content/docs/components/angle-slider.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/angle-slider" +npx shadcn@latest add @diceui/angle-slider ``` ### Manual diff --git a/docs/content/docs/components/avatar-group.mdx b/docs/content/docs/components/avatar-group.mdx index 8c046b80..a864fcb7 100644 --- a/docs/content/docs/components/avatar-group.mdx +++ b/docs/content/docs/components/avatar-group.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/avatar-group" +npx shadcn@latest add @diceui/avatar-group ``` diff --git a/docs/content/docs/components/badge-overflow.mdx b/docs/content/docs/components/badge-overflow.mdx index a0166b89..6831cef6 100644 --- a/docs/content/docs/components/badge-overflow.mdx +++ b/docs/content/docs/components/badge-overflow.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/badge-overflow" +npx shadcn@latest add @diceui/badge-overflow ``` diff --git a/docs/content/docs/components/checkbox-group.mdx b/docs/content/docs/components/checkbox-group.mdx index 40f6f0e9..0b4fcea1 100644 --- a/docs/content/docs/components/checkbox-group.mdx +++ b/docs/content/docs/components/checkbox-group.mdx @@ -19,7 +19,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/checkbox-group" +npx shadcn@latest add @diceui/checkbox-group ``` ### Manual diff --git a/docs/content/docs/components/circular-progress.mdx b/docs/content/docs/components/circular-progress.mdx index c9a8840b..ffdadeaf 100644 --- a/docs/content/docs/components/circular-progress.mdx +++ b/docs/content/docs/components/circular-progress.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/circular-progress" +npx shadcn@latest add @diceui/circular-progress ``` ### Manual diff --git a/docs/content/docs/components/color-picker.mdx b/docs/content/docs/components/color-picker.mdx index 1f54a199..a182e253 100644 --- a/docs/content/docs/components/color-picker.mdx +++ b/docs/content/docs/components/color-picker.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/color-picker" +npx shadcn@latest add @diceui/color-picker ``` diff --git a/docs/content/docs/components/color-swatch.mdx b/docs/content/docs/components/color-swatch.mdx index e41ad42c..782ddcf9 100644 --- a/docs/content/docs/components/color-swatch.mdx +++ b/docs/content/docs/components/color-swatch.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/color-swatch" +npx shadcn@latest add @diceui/color-swatch ``` diff --git a/docs/content/docs/components/combobox.mdx b/docs/content/docs/components/combobox.mdx index 14fdb596..24826664 100644 --- a/docs/content/docs/components/combobox.mdx +++ b/docs/content/docs/components/combobox.mdx @@ -19,7 +19,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/combobox" +npx shadcn@latest add @diceui/combobox ``` ### Manual @@ -148,7 +148,7 @@ A wrapper element that positions the combobox popover relative to the input and +/> diff --git a/docs/content/docs/components/compare-slider.mdx b/docs/content/docs/components/compare-slider.mdx index f238f920..20e9aac0 100644 --- a/docs/content/docs/components/compare-slider.mdx +++ b/docs/content/docs/components/compare-slider.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/compare-slider" +npx shadcn@latest add @diceui/compare-slider ``` ### Manual diff --git a/docs/content/docs/components/cropper.mdx b/docs/content/docs/components/cropper.mdx index 2ada2adb..c01eeb46 100644 --- a/docs/content/docs/components/cropper.mdx +++ b/docs/content/docs/components/cropper.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/cropper" +npx shadcn@latest add @diceui/cropper ``` diff --git a/docs/content/docs/components/editable.mdx b/docs/content/docs/components/editable.mdx index 11513297..6ac7cb21 100644 --- a/docs/content/docs/components/editable.mdx +++ b/docs/content/docs/components/editable.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/editable" +npx shadcn@latest add @diceui/editable ``` ### Manual diff --git a/docs/content/docs/components/file-upload.mdx b/docs/content/docs/components/file-upload.mdx index ff66706c..6eaccc8a 100644 --- a/docs/content/docs/components/file-upload.mdx +++ b/docs/content/docs/components/file-upload.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/file-upload" +npx shadcn@latest add @diceui/file-upload ``` ### Manual diff --git a/docs/content/docs/components/fps.mdx b/docs/content/docs/components/fps.mdx index a81e8d10..36a5f74f 100644 --- a/docs/content/docs/components/fps.mdx +++ b/docs/content/docs/components/fps.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/fps" +npx shadcn@latest add @diceui/fps ``` ### Manual diff --git a/docs/content/docs/components/kanban.mdx b/docs/content/docs/components/kanban.mdx index 2b8b4014..799f6f8b 100644 --- a/docs/content/docs/components/kanban.mdx +++ b/docs/content/docs/components/kanban.mdx @@ -14,7 +14,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/kanban" +npx shadcn@latest add @diceui/kanban ``` ### Manual diff --git a/docs/content/docs/components/key-value.mdx b/docs/content/docs/components/key-value.mdx index b4e0a359..ebc0ee6f 100644 --- a/docs/content/docs/components/key-value.mdx +++ b/docs/content/docs/components/key-value.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/key-value" +npx shadcn@latest add @diceui/key-value ``` ### Manual diff --git a/docs/content/docs/components/listbox.mdx b/docs/content/docs/components/listbox.mdx index 14ec58c9..cef1e0bf 100644 --- a/docs/content/docs/components/listbox.mdx +++ b/docs/content/docs/components/listbox.mdx @@ -19,7 +19,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/listbox" +npx shadcn@latest add @diceui/listbox ``` ### Manual diff --git a/docs/content/docs/components/marquee.mdx b/docs/content/docs/components/marquee.mdx index 28d3e02e..eff2d583 100644 --- a/docs/content/docs/components/marquee.mdx +++ b/docs/content/docs/components/marquee.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/marquee" +npx shadcn@latest add @diceui/marquee ``` ### Manual @@ -247,7 +247,7 @@ Edge overlay components for smooth gradient transitions. - **RTL Support**: Automatically adapts to RTL (right-to-left) layouts - **Screen Reader Support**: Content remains accessible to assistive technologies - **Reduced Motion**: Respects user's `prefers-reduced-motion` setting -- **Pause Controls**: +- **Pause Controls**: - **Hover**: Can be configured to pause animation when hovered - **Keyboard**: Press Space key to pause/resume (when `pauseOnKeyboard` is enabled) - **Focus Management**: Proper focus indicators and keyboard navigation when `pauseOnKeyboard` is enabled diff --git a/docs/content/docs/components/mask-input.mdx b/docs/content/docs/components/mask-input.mdx index ea95bea5..86230e3f 100644 --- a/docs/content/docs/components/mask-input.mdx +++ b/docs/content/docs/components/mask-input.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/mask-input" +npx shadcn@latest add @diceui/mask-input ``` ### Manual diff --git a/docs/content/docs/components/masonry.mdx b/docs/content/docs/components/masonry.mdx index 41f911ee..53f5e567 100644 --- a/docs/content/docs/components/masonry.mdx +++ b/docs/content/docs/components/masonry.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/masonry" +npx shadcn@latest add @diceui/masonry ``` ### Manual diff --git a/docs/content/docs/components/media-player.mdx b/docs/content/docs/components/media-player.mdx index ca23fe88..b241e1bb 100644 --- a/docs/content/docs/components/media-player.mdx +++ b/docs/content/docs/components/media-player.mdx @@ -58,7 +58,7 @@ links: After completing the prerequisite component updates, install the media player: ```package-install - npx shadcn@latest add "https://diceui.com/r/media-player" + npx shadcn@latest add @diceui/media-player ``` diff --git a/docs/content/docs/components/mention.mdx b/docs/content/docs/components/mention.mdx index 33e92c2c..3c9de1d6 100644 --- a/docs/content/docs/components/mention.mdx +++ b/docs/content/docs/components/mention.mdx @@ -19,7 +19,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/mention" +npx shadcn@latest add @diceui/mention ``` ### Manual diff --git a/docs/content/docs/components/qr-code.mdx b/docs/content/docs/components/qr-code.mdx index 1c9546b1..813dd978 100644 --- a/docs/content/docs/components/qr-code.mdx +++ b/docs/content/docs/components/qr-code.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/qr-code" +npx shadcn@latest add @diceui/qr-code ``` ### Manual diff --git a/docs/content/docs/components/rating.mdx b/docs/content/docs/components/rating.mdx index cb0736f5..ed33fc9e 100644 --- a/docs/content/docs/components/rating.mdx +++ b/docs/content/docs/components/rating.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/rating" +npx shadcn@latest add @diceui/rating ``` ### Manual diff --git a/docs/content/docs/components/relative-time-card.mdx b/docs/content/docs/components/relative-time-card.mdx index bea92010..268bb681 100644 --- a/docs/content/docs/components/relative-time-card.mdx +++ b/docs/content/docs/components/relative-time-card.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/relative-time-card" +npx shadcn@latest add @diceui/relative-time-card ``` ### Manual diff --git a/docs/content/docs/components/scroll-spy.mdx b/docs/content/docs/components/scroll-spy.mdx index ee36c7df..5a842fb6 100644 --- a/docs/content/docs/components/scroll-spy.mdx +++ b/docs/content/docs/components/scroll-spy.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/scroll-spy" +npx shadcn@latest add @diceui/scroll-spy ``` ### Manual diff --git a/docs/content/docs/components/scroller.mdx b/docs/content/docs/components/scroller.mdx index e8907b7b..d445c4a2 100644 --- a/docs/content/docs/components/scroller.mdx +++ b/docs/content/docs/components/scroller.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/scroller" +npx shadcn@latest add @diceui/scroller ``` ### Manual diff --git a/docs/content/docs/components/segmented-input.mdx b/docs/content/docs/components/segmented-input.mdx index ed67bcea..28f47763 100644 --- a/docs/content/docs/components/segmented-input.mdx +++ b/docs/content/docs/components/segmented-input.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/segmented-input" +npx shadcn@latest add @diceui/segmented-input ``` ### Manual diff --git a/docs/content/docs/components/sortable.mdx b/docs/content/docs/components/sortable.mdx index 489c4b35..fc9c529c 100644 --- a/docs/content/docs/components/sortable.mdx +++ b/docs/content/docs/components/sortable.mdx @@ -14,7 +14,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/sortable" +npx shadcn@latest add @diceui/sortable ``` diff --git a/docs/content/docs/components/stack.mdx b/docs/content/docs/components/stack.mdx index eb6d2754..2bb3e65e 100644 --- a/docs/content/docs/components/stack.mdx +++ b/docs/content/docs/components/stack.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/stack" +npx shadcn@latest add @diceui/stack ``` diff --git a/docs/content/docs/components/stat.mdx b/docs/content/docs/components/stat.mdx index 39968019..e4e25560 100644 --- a/docs/content/docs/components/stat.mdx +++ b/docs/content/docs/components/stat.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/stat" +npx shadcn@latest add @diceui/stat ``` ### Manual diff --git a/docs/content/docs/components/stepper.mdx b/docs/content/docs/components/stepper.mdx index b472f95d..e6a237e7 100644 --- a/docs/content/docs/components/stepper.mdx +++ b/docs/content/docs/components/stepper.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/stepper" +npx shadcn@latest add @diceui/stepper ``` diff --git a/docs/content/docs/components/tags-input.mdx b/docs/content/docs/components/tags-input.mdx index d2efacc9..edf47d16 100644 --- a/docs/content/docs/components/tags-input.mdx +++ b/docs/content/docs/components/tags-input.mdx @@ -19,7 +19,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/tags-input" +npx shadcn@latest add @diceui/tags-input ``` ### Manual diff --git a/docs/content/docs/components/time-picker.mdx b/docs/content/docs/components/time-picker.mdx index 51a4d388..eedcefdb 100644 --- a/docs/content/docs/components/time-picker.mdx +++ b/docs/content/docs/components/time-picker.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/time-picker" +npx shadcn@latest add @diceui/time-picker ``` ### Manual diff --git a/docs/content/docs/components/timeline.mdx b/docs/content/docs/components/timeline.mdx index 7579ce1c..818d76d9 100644 --- a/docs/content/docs/components/timeline.mdx +++ b/docs/content/docs/components/timeline.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/timeline" +npx shadcn@latest add @diceui/timeline ``` diff --git a/docs/content/docs/components/tour.mdx b/docs/content/docs/components/tour.mdx index 8876bece..b1ca3018 100644 --- a/docs/content/docs/components/tour.mdx +++ b/docs/content/docs/components/tour.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/tour" +npx shadcn@latest add @diceui/tour ``` diff --git a/docs/content/docs/utilities/client-only.mdx b/docs/content/docs/utilities/client-only.mdx index 4571e2ce..0a65f877 100644 --- a/docs/content/docs/utilities/client-only.mdx +++ b/docs/content/docs/utilities/client-only.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/client-only" +npx shadcn@latest add @diceui/client-only ``` ### Manual diff --git a/docs/content/docs/utilities/composition.mdx b/docs/content/docs/utilities/composition.mdx index 9a087131..6c91e338 100644 --- a/docs/content/docs/utilities/composition.mdx +++ b/docs/content/docs/utilities/composition.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/composition" +npx shadcn@latest add @diceui/composition ``` ### Manual diff --git a/docs/content/docs/utilities/direction-provider.mdx b/docs/content/docs/utilities/direction-provider.mdx index afc0732d..ea15d4a6 100644 --- a/docs/content/docs/utilities/direction-provider.mdx +++ b/docs/content/docs/utilities/direction-provider.mdx @@ -11,7 +11,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/direction-provider" +npx shadcn@latest add @diceui/direction-provider ``` ### Manual diff --git a/docs/content/docs/utilities/hitbox.mdx b/docs/content/docs/utilities/hitbox.mdx index 9f829176..0ded4bc3 100644 --- a/docs/content/docs/utilities/hitbox.mdx +++ b/docs/content/docs/utilities/hitbox.mdx @@ -13,7 +13,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/hitbox" +npx shadcn@latest add @diceui/hitbox ``` ### Manual diff --git a/docs/content/docs/utilities/portal.mdx b/docs/content/docs/utilities/portal.mdx index 153733bd..c8934b0d 100644 --- a/docs/content/docs/utilities/portal.mdx +++ b/docs/content/docs/utilities/portal.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/portal" +npx shadcn@latest add @diceui/portal ``` ### Manual diff --git a/docs/content/docs/utilities/presence.mdx b/docs/content/docs/utilities/presence.mdx index 29f0a4d8..4db72924 100644 --- a/docs/content/docs/utilities/presence.mdx +++ b/docs/content/docs/utilities/presence.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/presence" +npx shadcn@latest add @diceui/presence ``` ### Manual diff --git a/docs/content/docs/utilities/visually-hidden-input.mdx b/docs/content/docs/utilities/visually-hidden-input.mdx index 2316f740..8c0214a0 100644 --- a/docs/content/docs/utilities/visually-hidden-input.mdx +++ b/docs/content/docs/utilities/visually-hidden-input.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/visually-hidden-input" +npx shadcn@latest add @diceui/visually-hidden-input ``` ### Manual diff --git a/docs/content/docs/utilities/visually-hidden.mdx b/docs/content/docs/utilities/visually-hidden.mdx index c971d72d..cdf9abd6 100644 --- a/docs/content/docs/utilities/visually-hidden.mdx +++ b/docs/content/docs/utilities/visually-hidden.mdx @@ -10,7 +10,7 @@ links: ### CLI ```package-install -npx shadcn@latest add "https://diceui.com/r/visually-hidden" +npx shadcn@latest add @diceui/visually-hidden ``` ### Manual diff --git a/docs/public/r/.gitignore b/docs/public/r/.gitignore new file mode 100644 index 00000000..94a2dd14 --- /dev/null +++ b/docs/public/r/.gitignore @@ -0,0 +1 @@ +*.json \ No newline at end of file diff --git a/docs/scripts/build-registry.mts b/docs/scripts/build-registry.mts index c0e79ca4..c63d7f10 100644 --- a/docs/scripts/build-registry.mts +++ b/docs/scripts/build-registry.mts @@ -1,46 +1,46 @@ -import { existsSync, promises as fs, readFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import path from "node:path"; -import { cwd } from "node:process"; -import { rimraf } from "rimraf"; -import { Project, ScriptKind, SyntaxKind } from "ts-morph"; -import type { z } from "zod"; - -import { registry } from "../registry"; -import { baseColors } from "../registry/registry-base-colors"; -import { colorMapping, colors } from "../registry/registry-colors"; -import { styles } from "../registry/registry-styles"; +import { existsSync, promises as fs } from 'node:fs' +import { tmpdir } from 'node:os' +import path from 'node:path' +import { cwd } from 'node:process' +import { rimraf } from 'rimraf' +import { Project, ScriptKind, SyntaxKind } from 'ts-morph' +import type { z } from 'zod' + +import { registry } from '../registry' +import { baseColors } from '../registry/registry-base-colors' +import { colorMapping, colors } from '../registry/registry-colors' +import { styles } from '../registry/registry-styles' import { type Registry, type RegistryItem, registryItemSchema, type registryItemTypeSchema, registrySchema, -} from "../registry/schema"; -import { createTemplate } from "./create-template.mts"; -import { fixImport } from "./fix-imports.mts"; +} from '../registry/schema' +import { createTemplate } from './create-template.mts' +import { fixImport } from './fix-imports.mts' -const REGISTRY_PATH = path.join(process.cwd(), "public/r"); +const REGISTRY_PATH = path.join(process.cwd(), 'public/r') const REGISTRY_INDEX_WHITELIST: z.infer[] = [ - "registry:ui", - "registry:lib", - "registry:hook", - "registry:theme", - "registry:block", - "registry:example", - "registry:component", - "registry:internal", - "registry:style", -]; + 'registry:ui', + 'registry:lib', + 'registry:hook', + 'registry:theme', + 'registry:block', + 'registry:example', + 'registry:component', + 'registry:internal', + 'registry:style', +] const project = new Project({ compilerOptions: {}, -}); +}) async function createTempSourceFile(filename: string) { - const dir = await fs.mkdtemp(path.join(tmpdir(), "shadcn-")); - return path.join(dir, filename); + const dir = await fs.mkdtemp(path.join(tmpdir(), 'shadcn-')) + return path.join(dir, filename) } // ---------------------------------------------------------------------------- @@ -53,75 +53,75 @@ async function buildRegistry(registry: Registry) { import * as React from "react" export const Index: Record = { -`; +` for (const style of styles) { - index += ` "${style.name}": {`; + index += ` "${style.name}": {` // Build style index. for (const item of registry.items) { const resolveFiles = item.files?.map( (file) => `registry/${style.name}/${ - typeof file === "string" ? file : file.path - }`, - ); + typeof file === 'string' ? file : file.path + }` + ) if (!resolveFiles) { - continue; + continue } - const type = item.type.split(":")[1]; - let sourceFilename = ""; + const type = item.type.split(':')[1] + let sourceFilename = '' // biome-ignore lint/suspicious/noExplicitAny: chunks can contain various types from AST parsing - let chunks: any = []; - if (item.type === "registry:block") { - const file = resolveFiles[0]; - const filename = path.basename(file); - let raw: string; + let chunks: any = [] + if (item.type === 'registry:block') { + const file = resolveFiles[0] + const filename = path.basename(file) + let raw: string try { - raw = await fs.readFile(file, "utf8"); + raw = await fs.readFile(file, 'utf8') } catch (_error) { - continue; + continue } - const tempFile = await createTempSourceFile(filename); + const tempFile = await createTempSourceFile(filename) const sourceFile = project.createSourceFile(tempFile, raw, { scriptKind: ScriptKind.TSX, - }); + }) const description = sourceFile - .getVariableDeclaration("description") + .getVariableDeclaration('description') ?.getInitializerOrThrow() .asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue(); + .getLiteralValue() - item.description = description ?? ""; + item.description = description ?? '' // Find all imports. const imports = new Map< string, { - module: string; - text: string; - isDefault?: boolean; + module: string + text: string + isDefault?: boolean } - >(); + >() for (const node of sourceFile.getImportDeclarations()) { - const module = node.getModuleSpecifier().getLiteralValue(); + const module = node.getModuleSpecifier().getLiteralValue() for (const item of node.getNamedImports()) { imports.set(item.getText(), { module, text: node.getText(), - }); + }) } - const defaultImport = node.getDefaultImport(); + const defaultImport = node.getDefaultImport() if (defaultImport) { imports.set(defaultImport.getText(), { module, text: defaultImport.getText(), isDefault: true, - }); + }) } } @@ -129,111 +129,111 @@ export const Index: Record = { const components = sourceFile .getDescendantsOfKind(SyntaxKind.JsxOpeningElement) .filter((node) => { - return node.getAttribute("x-chunk") !== undefined; - }); + return node.getAttribute('x-chunk') !== undefined + }) chunks = await Promise.all( components.map(async (component, index) => { - const chunkName = `${item.name}-chunk-${index}`; + const chunkName = `${item.name}-chunk-${index}` // Get the value of x-chunk attribute. const attr = component - .getAttributeOrThrow("x-chunk") - .asKindOrThrow(SyntaxKind.JsxAttribute); + .getAttributeOrThrow('x-chunk') + .asKindOrThrow(SyntaxKind.JsxAttribute) const description = attr .getInitializerOrThrow() .asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue(); + .getLiteralValue() // Delete the x-chunk attribute. - attr.remove(); + attr.remove() // Add a new attribute to the component. component.addAttribute({ - name: "x-chunk", + name: 'x-chunk', initializer: `"${chunkName}"`, - }); + }) // Get the value of x-chunk-container attribute. const containerAttr = component - .getAttribute("x-chunk-container") - ?.asKindOrThrow(SyntaxKind.JsxAttribute); + .getAttribute('x-chunk-container') + ?.asKindOrThrow(SyntaxKind.JsxAttribute) const containerClassName = containerAttr ?.getInitializer() ?.asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue(); + .getLiteralValue() - containerAttr?.remove(); + containerAttr?.remove() const parentJsxElement = component.getParentIfKindOrThrow( - SyntaxKind.JsxElement, - ); + SyntaxKind.JsxElement + ) // Find all opening tags on component. const children = parentJsxElement .getDescendantsOfKind(SyntaxKind.JsxOpeningElement) .map((node) => { - return node.getTagNameNode().getText(); + return node.getTagNameNode().getText() }) .concat( parentJsxElement .getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement) .map((node) => { - return node.getTagNameNode().getText(); - }), - ); + return node.getTagNameNode().getText() + }) + ) const componentImports = new Map< string, string | string[] | Set - >(); + >() for (const child of children) { - const importLine = imports.get(child); + const importLine = imports.get(child) if (importLine) { - const imports = componentImports.get(importLine.module) || []; + const imports = componentImports.get(importLine.module) || [] const newImports = importLine.isDefault ? importLine.text - : new Set([...imports, child]); + : new Set([...imports, child]) componentImports.set( importLine.module, - importLine?.isDefault ? newImports : Array.from(newImports), - ); + importLine?.isDefault ? newImports : Array.from(newImports) + ) } } const componnetImportLines = Array.from( - componentImports.keys(), + componentImports.keys() ).map((key) => { - const values = componentImports.get(key); + const values = componentImports.get(key) const specifier = Array.isArray(values) - ? `{${values.join(",")}}` - : values; + ? `{${values.join(',')}}` + : values - return `import ${specifier} from "${key}"`; - }); + return `import ${specifier} from "${key}"` + }) const code = ` 'use client' - ${componnetImportLines.join("\n")} + ${componnetImportLines.join('\n')} export default function Component() { return (${parentJsxElement.getText()}) - }`; + }` - const targetFile = file.replace(item.name, `${chunkName}`); + const targetFile = file.replace(item.name, `${chunkName}`) const targetFilePath = path.join( cwd(), - `registry/${style.name}/${type}/${chunkName}.tsx`, - ); + `registry/${style.name}/${type}/${chunkName}.tsx` + ) // Write component file. - rimraf.sync(targetFilePath); - await fs.writeFile(targetFilePath, code, "utf8"); + rimraf.sync(targetFilePath) + await fs.writeFile(targetFilePath, code, 'utf8') return { name: chunkName, @@ -243,43 +243,43 @@ export const Index: Record = { container: { className: containerClassName, }, - }; - }), - ); + } + }) + ) // // Write the source file for blocks only. - sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`; + sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx` if (item.files) { const files = item.files.map((file) => - typeof file === "string" - ? { type: "registry:page", path: file } - : file, - ); + typeof file === 'string' + ? { type: 'registry:page', path: file } + : file + ) if (files?.length) { - sourceFilename = `__registry__/${style.name}/${files[0].path}`; + sourceFilename = `__registry__/${style.name}/${files[0].path}` } } - const sourcePath = path.join(process.cwd(), sourceFilename); + const sourcePath = path.join(process.cwd(), sourceFilename) if (!existsSync(sourcePath)) { - await fs.mkdir(sourcePath, { recursive: true }); + await fs.mkdir(sourcePath, { recursive: true }) } - rimraf.sync(sourcePath); - await fs.writeFile(sourcePath, sourceFile.getText()); + rimraf.sync(sourcePath) + await fs.writeFile(sourcePath, sourceFile.getText()) } - let componentPath = `@/registry/${style.name}/${type}/${item.name}`; + let componentPath = `@/registry/${style.name}/${type}/${item.name}` if (item.files) { const files = item.files.map((file) => - typeof file === "string" - ? { type: "registry:page", path: file } - : file, - ); + typeof file === 'string' + ? { type: 'registry:page', path: file } + : file + ) if (files?.length) { - componentPath = `@/registry/${style.name}/${files[0].path}`; + componentPath = `@/registry/${style.name}/${files[0].path}` } } @@ -287,87 +287,87 @@ export const Index: Record = { const shouldGenerateComponent = item.files && item.files.length > 0 && - !["index", "style"].includes(item.name); + !['index', 'style'].includes(item.name) index += ` "${item.name}": { name: "${item.name}", - description: "${item.description ?? ""}", + description: "${item.description ?? ''}", type: "${item.type}", registryDependencies: ${JSON.stringify(item.registryDependencies)}, files: [${item.files?.map((file) => { const filePath = `registry/${style.name}/${ - typeof file === "string" ? file : file.path - }`; - const resolvedFilePath = path.resolve(filePath); - return typeof file === "string" + typeof file === 'string' ? file : file.path + }` + const resolvedFilePath = path.resolve(filePath) + return typeof file === 'string' ? `"${resolvedFilePath}"` : `{ path: "${filePath}", type: "${file.type}", - target: "${file.target ?? ""}" - }`; + target: "${file.target ?? ''}" + }` })}],${ shouldGenerateComponent ? ` component: React.lazy(() => import("${componentPath}")),` - : "" + : '' } source: "${sourceFilename}", chunks: [${chunks.map( (chunk) => `{ name: "${chunk.name}", - description: "${chunk.description ?? "No description"}", + description: "${chunk.description ?? 'No description'}", component: ${chunk.component} file: "${chunk.file}", container: { className: "${chunk.container.className}" } - }`, + }` )}] - },`; + },` } index += ` - },`; + },` } index += ` } -`; +` // ---------------------------------------------------------------------------- // Build registry/index.json. // ---------------------------------------------------------------------------- const items = registry.items - .filter((item) => ["registry:ui"].includes(item.type)) + .filter((item) => ['registry:ui'].includes(item.type)) .map((item) => { return { ...item, files: item.files?.map((_file) => { const file = - typeof _file === "string" + typeof _file === 'string' ? { path: _file, type: item.type, } - : _file; + : _file - return file; + return file }), - }; - }); - const registryJson = JSON.stringify(items, null, 2); - rimraf.sync(path.join(REGISTRY_PATH, "index.json")); + } + }) + const registryJson = JSON.stringify(items, null, 2) + rimraf.sync(path.join(REGISTRY_PATH, 'index.json')) await fs.writeFile( - path.join(REGISTRY_PATH, "index.json"), + path.join(REGISTRY_PATH, 'index.json'), registryJson, - "utf8", - ); + 'utf8' + ) // Write style index. - rimraf.sync(path.join(process.cwd(), "__registry__/index.tsx")); - await fs.writeFile(path.join(process.cwd(), "__registry__/index.tsx"), index); + rimraf.sync(path.join(process.cwd(), '__registry__/index.tsx')) + await fs.writeFile(path.join(process.cwd(), '__registry__/index.tsx'), index) } // ---------------------------------------------------------------------------- @@ -375,79 +375,79 @@ export const Index: Record = { // ---------------------------------------------------------------------------- async function buildStyles(registry: Registry) { for (const style of styles) { - const targetPath = path.join(REGISTRY_PATH, "styles", style.name); + const targetPath = path.join(REGISTRY_PATH, 'styles', style.name) // Create directory if it doesn't exist. if (!existsSync(targetPath)) { - await fs.mkdir(targetPath, { recursive: true }); + await fs.mkdir(targetPath, { recursive: true }) } for (const item of registry.items) { if (!REGISTRY_INDEX_WHITELIST.includes(item.type)) { - continue; + continue } // biome-ignore lint/suspicious/noExplicitAny: files array can contain various registry item types - let files: any[] = []; + let files: any[] = [] if (item.files) { files = await Promise.all( item.files.map(async (_file) => { const file = - typeof _file === "string" + typeof _file === 'string' ? { path: _file, type: item.type, - content: "", - target: "", + content: '', + target: '', } - : _file; + : _file - let content: string; + let content: string try { content = await fs.readFile( - path.join(process.cwd(), "registry", style.name, file.path), - "utf8", - ); + path.join(process.cwd(), 'registry', style.name, file.path), + 'utf8' + ) // Only fix imports for v0- blocks. - if (item.name.startsWith("v0-")) { - content = fixImport(content); + if (item.name.startsWith('v0-')) { + content = fixImport(content) } } catch (_error) { - return; + return } - const tempFile = await createTempSourceFile(file.path); + const tempFile = await createTempSourceFile(file.path) const sourceFile = project.createSourceFile(tempFile, content, { scriptKind: ScriptKind.TSX, - }); + }) - sourceFile.getVariableDeclaration("iframeHeight")?.remove(); - sourceFile.getVariableDeclaration("containerClassName")?.remove(); - sourceFile.getVariableDeclaration("description")?.remove(); + sourceFile.getVariableDeclaration('iframeHeight')?.remove() + sourceFile.getVariableDeclaration('containerClassName')?.remove() + sourceFile.getVariableDeclaration('description')?.remove() - let target = file.target || ""; + let target = file.target || '' - if ((!target || target === "") && item.name.startsWith("v0-")) { - const fileName = file.path.split("/").pop(); + if ((!target || target === '') && item.name.startsWith('v0-')) { + const fileName = file.path.split('/').pop() if ( - file.type === "registry:block" || - file.type === "registry:component" || - file.type === "registry:example" + file.type === 'registry:block' || + file.type === 'registry:component' || + file.type === 'registry:example' ) { - target = `components/${fileName}`; + target = `components/${fileName}` } - if (file.type === "registry:ui") { - target = `components/ui/${fileName}`; + if (file.type === 'registry:ui') { + target = `components/ui/${fileName}` } - if (file.type === "registry:hook") { - target = `hooks/${fileName}`; + if (file.type === 'registry:hook') { + target = `hooks/${fileName}` } - if (file.type === "registry:lib") { - target = `lib/${fileName}`; + if (file.type === 'registry:lib') { + target = `lib/${fileName}` } } @@ -456,22 +456,30 @@ async function buildStyles(registry: Registry) { type: file.type, content: sourceFile.getText(), target, - }; - }), - ); + } + }) + ) } const payload = registryItemSchema.safeParse({ ...item, files, - }); + }) if (payload.success) { + // Write to styles subdirectory (existing behavior) await fs.writeFile( path.join(targetPath, `${item.name}.json`), JSON.stringify(payload.data, null, 2), - "utf8", - ); + 'utf8' + ) + + // Write to root registry path: public/r/{item-name}.json + await fs.writeFile( + path.join(REGISTRY_PATH, `${item.name}.json`), + JSON.stringify(payload.data, null, 2), + 'utf8' + ) } } } @@ -479,12 +487,12 @@ async function buildStyles(registry: Registry) { // ---------------------------------------------------------------------------- // Build registry/styles/index.json. // ---------------------------------------------------------------------------- - const stylesJson = JSON.stringify(styles, null, 2); + const stylesJson = JSON.stringify(styles, null, 2) await fs.writeFile( - path.join(REGISTRY_PATH, "styles/index.json"), + path.join(REGISTRY_PATH, 'styles/index.json'), stylesJson, - "utf8", - ); + 'utf8' + ) } // ---------------------------------------------------------------------------- @@ -492,13 +500,13 @@ async function buildStyles(registry: Registry) { // ---------------------------------------------------------------------------- async function buildStylesIndex() { for (const style of styles) { - const targetPath = path.join(REGISTRY_PATH, "styles", style.name); + const targetPath = path.join(REGISTRY_PATH, 'styles', style.name) const dependencies = [ - "tailwindcss-animate", - "class-variance-authority", - "lucide-react", - ]; + 'tailwindcss-animate', + 'class-variance-authority', + 'lucide-react', + ] // TODO: Remove this when we migrate to lucide-react. // if (style.name === "new-york") { @@ -507,9 +515,9 @@ async function buildStylesIndex() { const payload: RegistryItem = { name: style.name, - type: "registry:style", + type: 'registry:style', dependencies, - registryDependencies: ["utils"], + registryDependencies: ['utils'], tailwind: { config: { plugins: [`require("tailwindcss-animate")`], @@ -517,13 +525,13 @@ async function buildStylesIndex() { }, cssVars: {}, files: [], - }; + } await fs.writeFile( - path.join(targetPath, "index.json"), + path.join(targetPath, 'index.json'), JSON.stringify(payload, null, 2), - "utf8", - ); + 'utf8' + ) } } @@ -531,49 +539,49 @@ async function buildStylesIndex() { // Build registry/colors/index.json. // ---------------------------------------------------------------------------- async function buildThemes() { - const colorsTargetPath = path.join(REGISTRY_PATH, "colors"); - rimraf.sync(colorsTargetPath); + const colorsTargetPath = path.join(REGISTRY_PATH, 'colors') + rimraf.sync(colorsTargetPath) if (!existsSync(colorsTargetPath)) { - await fs.mkdir(colorsTargetPath, { recursive: true }); + await fs.mkdir(colorsTargetPath, { recursive: true }) } // biome-ignore lint/suspicious/noExplicitAny: color data structure varies by color type - const colorsData: Record = {}; + const colorsData: Record = {} for (const [color, value] of Object.entries(colors)) { - if (typeof value === "string") { - colorsData[color] = value; - continue; + if (typeof value === 'string') { + colorsData[color] = value + continue } if (Array.isArray(value)) { colorsData[color] = value.map((item) => ({ ...item, - rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, "$1 $2 $3"), + rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'), hslChannel: item.hsl.replace( /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/, - "$1 $2 $3", + '$1 $2 $3' ), - })); - continue; + })) + continue } - if (typeof value === "object") { + if (typeof value === 'object') { colorsData[color] = { ...value, - rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, "$1 $2 $3"), + rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'), hslChannel: value.hsl.replace( /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/, - "$1 $2 $3", + '$1 $2 $3' ), - }; + } } } await fs.writeFile( - path.join(colorsTargetPath, "index.json"), + path.join(colorsTargetPath, 'index.json'), JSON.stringify(colorsData, null, 2), - "utf8", - ); + 'utf8' + ) // ---------------------------------------------------------------------------- // Build registry/colors/[base].json. @@ -581,7 +589,7 @@ async function buildThemes() { const BASE_STYLES = `@tailwind base; @tailwind components; @tailwind utilities; - `; + ` const BASE_STYLES_WITH_VARIABLES = `@tailwind base; @tailwind components; @@ -651,53 +659,53 @@ async function buildThemes() { body { @apply bg-background text-foreground; } -}`; +}` - for (const baseColor of ["slate", "gray", "zinc", "neutral", "stone"]) { + for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) { // biome-ignore lint/suspicious/noExplicitAny: color data structure varies by color type const base: Record = { inlineColors: {}, cssVars: {}, - }; + } for (const [mode, values] of Object.entries(colorMapping)) { - base.inlineColors[mode] = {}; - base.cssVars[mode] = {}; + base.inlineColors[mode] = {} + base.cssVars[mode] = {} for (const [key, value] of Object.entries(values)) { - if (typeof value === "string") { + if (typeof value === 'string') { // Chart colors do not have a 1-to-1 mapping with tailwind colors. - if (key.startsWith("chart-")) { - base.cssVars[mode][key] = value; - continue; + if (key.startsWith('chart-')) { + base.cssVars[mode][key] = value + continue } - const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`); - base.inlineColors[mode][key] = resolvedColor; + const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`) + base.inlineColors[mode][key] = resolvedColor - const [resolvedBase, scale] = resolvedColor.split("-"); + const [resolvedBase, scale] = resolvedColor.split('-') const color = scale ? colorsData[resolvedBase].find( // biome-ignore lint/suspicious/noExplicitAny: color items have dynamic structure - (item: any) => item.scale === Number.parseInt(scale, 10), + (item: any) => item.scale === Number.parseInt(scale, 10) ) - : colorsData[resolvedBase]; + : colorsData[resolvedBase] if (color) { - base.cssVars[mode][key] = color.hslChannel; + base.cssVars[mode][key] = color.hslChannel } } } } // Build css vars. - base.inlineColorsTemplate = createTemplate(BASE_STYLES)({}); + base.inlineColorsTemplate = createTemplate(BASE_STYLES)({}) base.cssVarsTemplate = createTemplate(BASE_STYLES_WITH_VARIABLES)({ colors: base.cssVars, - }); + }) await fs.writeFile( path.join(REGISTRY_PATH, `colors/${baseColor}.json`), JSON.stringify(base, null, 2), - "utf8", - ); + 'utf8' + ) // ---------------------------------------------------------------------------- // Build registry/themes.css @@ -765,88 +773,88 @@ async function buildThemes() { --destructive-foreground: <%- colors.dark["destructive-foreground"] %>; --ring: <%- colors.dark["ring"] %>; -}`; +}` - const themeCSS = []; + const themeCSS = [] for (const theme of baseColors) { themeCSS.push( // @ts-expect-error createTemplate(THEME_STYLES_WITH_VARIABLES)({ colors: theme.cssVars, theme: theme.name, - }), - ); + }) + ) } await fs.writeFile( - path.join(REGISTRY_PATH, "themes.css"), - themeCSS.join("\n"), - "utf8", - ); + path.join(REGISTRY_PATH, 'themes.css'), + themeCSS.join('\n'), + 'utf8' + ) // ---------------------------------------------------------------------------- // Build registry/themes/[theme].json // ---------------------------------------------------------------------------- - rimraf.sync(path.join(REGISTRY_PATH, "themes")); - for (const baseColor of ["slate", "gray", "zinc", "neutral", "stone"]) { + rimraf.sync(path.join(REGISTRY_PATH, 'themes')) + for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) { // biome-ignore lint/suspicious/noExplicitAny: theme payload structure varies by theme type const payload: Record = { name: baseColor, label: baseColor.charAt(0).toUpperCase() + baseColor.slice(1), cssVars: {}, - }; + } for (const [mode, values] of Object.entries(colorMapping)) { - payload.cssVars[mode] = {}; + payload.cssVars[mode] = {} for (const [key, value] of Object.entries(values)) { - if (typeof value === "string") { - const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`); - payload.cssVars[mode][key] = resolvedColor; + if (typeof value === 'string') { + const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`) + payload.cssVars[mode][key] = resolvedColor - const [resolvedBase, scale] = resolvedColor.split("-"); + const [resolvedBase, scale] = resolvedColor.split('-') const color = scale ? colorsData[resolvedBase].find( // biome-ignore lint/suspicious/noExplicitAny: color items have dynamic structure - (item: any) => item.scale === Number.parseInt(scale, 10), + (item: any) => item.scale === Number.parseInt(scale, 10) ) - : colorsData[resolvedBase]; + : colorsData[resolvedBase] if (color) { - payload.cssVars[mode][key] = color.hslChannel; + payload.cssVars[mode][key] = color.hslChannel } } } } - const targetPath = path.join(REGISTRY_PATH, "themes"); + const targetPath = path.join(REGISTRY_PATH, 'themes') // Create directory if it doesn't exist. if (!existsSync(targetPath)) { - await fs.mkdir(targetPath, { recursive: true }); + await fs.mkdir(targetPath, { recursive: true }) } await fs.writeFile( path.join(targetPath, `${payload.name}.json`), JSON.stringify(payload, null, 2), - "utf8", - ); + 'utf8' + ) } } } try { - const result = registrySchema.safeParse(registry); + const result = registrySchema.safeParse(registry) if (!result.success) { - console.error(result.error); - process.exit(1); + console.error(result.error) + process.exit(1) } - await buildRegistry(result.data); - await buildStyles(result.data); - await buildStylesIndex(); - await buildThemes(); + await buildRegistry(result.data) + await buildStyles(result.data) + await buildStylesIndex() + await buildThemes() - console.log("✅ Done!"); + console.log('✅ Done!') } catch (error) { - console.error(error); - process.exit(1); + console.error(error) + process.exit(1) } From 4133a9d60fb894c100e560648bb781ceddc36172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Best?= Date: Tue, 2 Dec 2025 17:11:20 +0100 Subject: [PATCH 2/2] chore: revert formatting back to Biome --- docs/scripts/build-registry.mts | 536 ++++++++++++++++---------------- 1 file changed, 268 insertions(+), 268 deletions(-) diff --git a/docs/scripts/build-registry.mts b/docs/scripts/build-registry.mts index c63d7f10..7593a6bc 100644 --- a/docs/scripts/build-registry.mts +++ b/docs/scripts/build-registry.mts @@ -1,46 +1,46 @@ -import { existsSync, promises as fs } from 'node:fs' -import { tmpdir } from 'node:os' -import path from 'node:path' -import { cwd } from 'node:process' -import { rimraf } from 'rimraf' -import { Project, ScriptKind, SyntaxKind } from 'ts-morph' -import type { z } from 'zod' - -import { registry } from '../registry' -import { baseColors } from '../registry/registry-base-colors' -import { colorMapping, colors } from '../registry/registry-colors' -import { styles } from '../registry/registry-styles' +import { existsSync, promises as fs } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { cwd } from "node:process"; +import { rimraf } from "rimraf"; +import { Project, ScriptKind, SyntaxKind } from "ts-morph"; +import type { z } from "zod"; + +import { registry } from "../registry"; +import { baseColors } from "../registry/registry-base-colors"; +import { colorMapping, colors } from "../registry/registry-colors"; +import { styles } from "../registry/registry-styles"; import { type Registry, type RegistryItem, registryItemSchema, type registryItemTypeSchema, registrySchema, -} from '../registry/schema' -import { createTemplate } from './create-template.mts' -import { fixImport } from './fix-imports.mts' +} from "../registry/schema"; +import { createTemplate } from "./create-template.mts"; +import { fixImport } from "./fix-imports.mts"; -const REGISTRY_PATH = path.join(process.cwd(), 'public/r') +const REGISTRY_PATH = path.join(process.cwd(), "public/r"); const REGISTRY_INDEX_WHITELIST: z.infer[] = [ - 'registry:ui', - 'registry:lib', - 'registry:hook', - 'registry:theme', - 'registry:block', - 'registry:example', - 'registry:component', - 'registry:internal', - 'registry:style', -] + "registry:ui", + "registry:lib", + "registry:hook", + "registry:theme", + "registry:block", + "registry:example", + "registry:component", + "registry:internal", + "registry:style", +]; const project = new Project({ compilerOptions: {}, -}) +}); async function createTempSourceFile(filename: string) { - const dir = await fs.mkdtemp(path.join(tmpdir(), 'shadcn-')) - return path.join(dir, filename) + const dir = await fs.mkdtemp(path.join(tmpdir(), "shadcn-")); + return path.join(dir, filename); } // ---------------------------------------------------------------------------- @@ -53,75 +53,75 @@ async function buildRegistry(registry: Registry) { import * as React from "react" export const Index: Record = { -` +`; for (const style of styles) { - index += ` "${style.name}": {` + index += ` "${style.name}": {`; // Build style index. for (const item of registry.items) { const resolveFiles = item.files?.map( (file) => `registry/${style.name}/${ - typeof file === 'string' ? file : file.path - }` - ) + typeof file === "string" ? file : file.path + }`, + ); if (!resolveFiles) { - continue + continue; } - const type = item.type.split(':')[1] - let sourceFilename = '' + const type = item.type.split(":")[1]; + let sourceFilename = ""; // biome-ignore lint/suspicious/noExplicitAny: chunks can contain various types from AST parsing - let chunks: any = [] - if (item.type === 'registry:block') { - const file = resolveFiles[0] - const filename = path.basename(file) - let raw: string + let chunks: any = []; + if (item.type === "registry:block") { + const file = resolveFiles[0]; + const filename = path.basename(file); + let raw: string; try { - raw = await fs.readFile(file, 'utf8') + raw = await fs.readFile(file, "utf8"); } catch (_error) { - continue + continue; } - const tempFile = await createTempSourceFile(filename) + const tempFile = await createTempSourceFile(filename); const sourceFile = project.createSourceFile(tempFile, raw, { scriptKind: ScriptKind.TSX, - }) + }); const description = sourceFile - .getVariableDeclaration('description') + .getVariableDeclaration("description") ?.getInitializerOrThrow() .asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue() + .getLiteralValue(); - item.description = description ?? '' + item.description = description ?? ""; // Find all imports. const imports = new Map< string, { - module: string - text: string - isDefault?: boolean + module: string; + text: string; + isDefault?: boolean; } - >() + >(); for (const node of sourceFile.getImportDeclarations()) { - const module = node.getModuleSpecifier().getLiteralValue() + const module = node.getModuleSpecifier().getLiteralValue(); for (const item of node.getNamedImports()) { imports.set(item.getText(), { module, text: node.getText(), - }) + }); } - const defaultImport = node.getDefaultImport() + const defaultImport = node.getDefaultImport(); if (defaultImport) { imports.set(defaultImport.getText(), { module, text: defaultImport.getText(), isDefault: true, - }) + }); } } @@ -129,111 +129,111 @@ export const Index: Record = { const components = sourceFile .getDescendantsOfKind(SyntaxKind.JsxOpeningElement) .filter((node) => { - return node.getAttribute('x-chunk') !== undefined - }) + return node.getAttribute("x-chunk") !== undefined; + }); chunks = await Promise.all( components.map(async (component, index) => { - const chunkName = `${item.name}-chunk-${index}` + const chunkName = `${item.name}-chunk-${index}`; // Get the value of x-chunk attribute. const attr = component - .getAttributeOrThrow('x-chunk') - .asKindOrThrow(SyntaxKind.JsxAttribute) + .getAttributeOrThrow("x-chunk") + .asKindOrThrow(SyntaxKind.JsxAttribute); const description = attr .getInitializerOrThrow() .asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue() + .getLiteralValue(); // Delete the x-chunk attribute. - attr.remove() + attr.remove(); // Add a new attribute to the component. component.addAttribute({ - name: 'x-chunk', + name: "x-chunk", initializer: `"${chunkName}"`, - }) + }); // Get the value of x-chunk-container attribute. const containerAttr = component - .getAttribute('x-chunk-container') - ?.asKindOrThrow(SyntaxKind.JsxAttribute) + .getAttribute("x-chunk-container") + ?.asKindOrThrow(SyntaxKind.JsxAttribute); const containerClassName = containerAttr ?.getInitializer() ?.asKindOrThrow(SyntaxKind.StringLiteral) - .getLiteralValue() + .getLiteralValue(); - containerAttr?.remove() + containerAttr?.remove(); const parentJsxElement = component.getParentIfKindOrThrow( - SyntaxKind.JsxElement - ) + SyntaxKind.JsxElement, + ); // Find all opening tags on component. const children = parentJsxElement .getDescendantsOfKind(SyntaxKind.JsxOpeningElement) .map((node) => { - return node.getTagNameNode().getText() + return node.getTagNameNode().getText(); }) .concat( parentJsxElement .getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement) .map((node) => { - return node.getTagNameNode().getText() - }) - ) + return node.getTagNameNode().getText(); + }), + ); const componentImports = new Map< string, string | string[] | Set - >() + >(); for (const child of children) { - const importLine = imports.get(child) + const importLine = imports.get(child); if (importLine) { - const imports = componentImports.get(importLine.module) || [] + const imports = componentImports.get(importLine.module) || []; const newImports = importLine.isDefault ? importLine.text - : new Set([...imports, child]) + : new Set([...imports, child]); componentImports.set( importLine.module, - importLine?.isDefault ? newImports : Array.from(newImports) - ) + importLine?.isDefault ? newImports : Array.from(newImports), + ); } } const componnetImportLines = Array.from( - componentImports.keys() + componentImports.keys(), ).map((key) => { - const values = componentImports.get(key) + const values = componentImports.get(key); const specifier = Array.isArray(values) - ? `{${values.join(',')}}` - : values + ? `{${values.join(",")}}` + : values; - return `import ${specifier} from "${key}"` - }) + return `import ${specifier} from "${key}"`; + }); const code = ` 'use client' - ${componnetImportLines.join('\n')} + ${componnetImportLines.join("\n")} export default function Component() { return (${parentJsxElement.getText()}) - }` + }`; - const targetFile = file.replace(item.name, `${chunkName}`) + const targetFile = file.replace(item.name, `${chunkName}`); const targetFilePath = path.join( cwd(), - `registry/${style.name}/${type}/${chunkName}.tsx` - ) + `registry/${style.name}/${type}/${chunkName}.tsx`, + ); // Write component file. - rimraf.sync(targetFilePath) - await fs.writeFile(targetFilePath, code, 'utf8') + rimraf.sync(targetFilePath); + await fs.writeFile(targetFilePath, code, "utf8"); return { name: chunkName, @@ -243,43 +243,43 @@ export const Index: Record = { container: { className: containerClassName, }, - } - }) - ) + }; + }), + ); // // Write the source file for blocks only. - sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx` + sourceFilename = `__registry__/${style.name}/${type}/${item.name}.tsx`; if (item.files) { const files = item.files.map((file) => - typeof file === 'string' - ? { type: 'registry:page', path: file } - : file - ) + typeof file === "string" + ? { type: "registry:page", path: file } + : file, + ); if (files?.length) { - sourceFilename = `__registry__/${style.name}/${files[0].path}` + sourceFilename = `__registry__/${style.name}/${files[0].path}`; } } - const sourcePath = path.join(process.cwd(), sourceFilename) + const sourcePath = path.join(process.cwd(), sourceFilename); if (!existsSync(sourcePath)) { - await fs.mkdir(sourcePath, { recursive: true }) + await fs.mkdir(sourcePath, { recursive: true }); } - rimraf.sync(sourcePath) - await fs.writeFile(sourcePath, sourceFile.getText()) + rimraf.sync(sourcePath); + await fs.writeFile(sourcePath, sourceFile.getText()); } - let componentPath = `@/registry/${style.name}/${type}/${item.name}` + let componentPath = `@/registry/${style.name}/${type}/${item.name}`; if (item.files) { const files = item.files.map((file) => - typeof file === 'string' - ? { type: 'registry:page', path: file } - : file - ) + typeof file === "string" + ? { type: "registry:page", path: file } + : file, + ); if (files?.length) { - componentPath = `@/registry/${style.name}/${files[0].path}` + componentPath = `@/registry/${style.name}/${files[0].path}`; } } @@ -287,87 +287,87 @@ export const Index: Record = { const shouldGenerateComponent = item.files && item.files.length > 0 && - !['index', 'style'].includes(item.name) + !["index", "style"].includes(item.name); index += ` "${item.name}": { name: "${item.name}", - description: "${item.description ?? ''}", + description: "${item.description ?? ""}", type: "${item.type}", registryDependencies: ${JSON.stringify(item.registryDependencies)}, files: [${item.files?.map((file) => { const filePath = `registry/${style.name}/${ - typeof file === 'string' ? file : file.path - }` - const resolvedFilePath = path.resolve(filePath) - return typeof file === 'string' + typeof file === "string" ? file : file.path + }`; + const resolvedFilePath = path.resolve(filePath); + return typeof file === "string" ? `"${resolvedFilePath}"` : `{ path: "${filePath}", type: "${file.type}", - target: "${file.target ?? ''}" - }` + target: "${file.target ?? ""}" + }`; })}],${ shouldGenerateComponent ? ` component: React.lazy(() => import("${componentPath}")),` - : '' + : "" } source: "${sourceFilename}", chunks: [${chunks.map( (chunk) => `{ name: "${chunk.name}", - description: "${chunk.description ?? 'No description'}", + description: "${chunk.description ?? "No description"}", component: ${chunk.component} file: "${chunk.file}", container: { className: "${chunk.container.className}" } - }` + }`, )}] - },` + },`; } index += ` - },` + },`; } index += ` } -` +`; // ---------------------------------------------------------------------------- // Build registry/index.json. // ---------------------------------------------------------------------------- const items = registry.items - .filter((item) => ['registry:ui'].includes(item.type)) + .filter((item) => ["registry:ui"].includes(item.type)) .map((item) => { return { ...item, files: item.files?.map((_file) => { const file = - typeof _file === 'string' + typeof _file === "string" ? { path: _file, type: item.type, } - : _file + : _file; - return file + return file; }), - } - }) - const registryJson = JSON.stringify(items, null, 2) - rimraf.sync(path.join(REGISTRY_PATH, 'index.json')) + }; + }); + const registryJson = JSON.stringify(items, null, 2); + rimraf.sync(path.join(REGISTRY_PATH, "index.json")); await fs.writeFile( - path.join(REGISTRY_PATH, 'index.json'), + path.join(REGISTRY_PATH, "index.json"), registryJson, - 'utf8' - ) + "utf8", + ); // Write style index. - rimraf.sync(path.join(process.cwd(), '__registry__/index.tsx')) - await fs.writeFile(path.join(process.cwd(), '__registry__/index.tsx'), index) + rimraf.sync(path.join(process.cwd(), "__registry__/index.tsx")); + await fs.writeFile(path.join(process.cwd(), "__registry__/index.tsx"), index); } // ---------------------------------------------------------------------------- @@ -375,79 +375,79 @@ export const Index: Record = { // ---------------------------------------------------------------------------- async function buildStyles(registry: Registry) { for (const style of styles) { - const targetPath = path.join(REGISTRY_PATH, 'styles', style.name) + const targetPath = path.join(REGISTRY_PATH, "styles", style.name); // Create directory if it doesn't exist. if (!existsSync(targetPath)) { - await fs.mkdir(targetPath, { recursive: true }) + await fs.mkdir(targetPath, { recursive: true }); } for (const item of registry.items) { if (!REGISTRY_INDEX_WHITELIST.includes(item.type)) { - continue + continue; } // biome-ignore lint/suspicious/noExplicitAny: files array can contain various registry item types - let files: any[] = [] + let files: any[] = []; if (item.files) { files = await Promise.all( item.files.map(async (_file) => { const file = - typeof _file === 'string' + typeof _file === "string" ? { path: _file, type: item.type, - content: '', - target: '', + content: "", + target: "", } - : _file + : _file; - let content: string + let content: string; try { content = await fs.readFile( - path.join(process.cwd(), 'registry', style.name, file.path), - 'utf8' - ) + path.join(process.cwd(), "registry", style.name, file.path), + "utf8", + ); // Only fix imports for v0- blocks. - if (item.name.startsWith('v0-')) { - content = fixImport(content) + if (item.name.startsWith("v0-")) { + content = fixImport(content); } } catch (_error) { - return + return; } - const tempFile = await createTempSourceFile(file.path) + const tempFile = await createTempSourceFile(file.path); const sourceFile = project.createSourceFile(tempFile, content, { scriptKind: ScriptKind.TSX, - }) + }); - sourceFile.getVariableDeclaration('iframeHeight')?.remove() - sourceFile.getVariableDeclaration('containerClassName')?.remove() - sourceFile.getVariableDeclaration('description')?.remove() + sourceFile.getVariableDeclaration("iframeHeight")?.remove(); + sourceFile.getVariableDeclaration("containerClassName")?.remove(); + sourceFile.getVariableDeclaration("description")?.remove(); - let target = file.target || '' + let target = file.target || ""; - if ((!target || target === '') && item.name.startsWith('v0-')) { - const fileName = file.path.split('/').pop() + if ((!target || target === "") && item.name.startsWith("v0-")) { + const fileName = file.path.split("/").pop(); if ( - file.type === 'registry:block' || - file.type === 'registry:component' || - file.type === 'registry:example' + file.type === "registry:block" || + file.type === "registry:component" || + file.type === "registry:example" ) { - target = `components/${fileName}` + target = `components/${fileName}`; } - if (file.type === 'registry:ui') { - target = `components/ui/${fileName}` + if (file.type === "registry:ui") { + target = `components/ui/${fileName}`; } - if (file.type === 'registry:hook') { - target = `hooks/${fileName}` + if (file.type === "registry:hook") { + target = `hooks/${fileName}`; } - if (file.type === 'registry:lib') { - target = `lib/${fileName}` + if (file.type === "registry:lib") { + target = `lib/${fileName}`; } } @@ -456,30 +456,30 @@ async function buildStyles(registry: Registry) { type: file.type, content: sourceFile.getText(), target, - } - }) - ) + }; + }), + ); } const payload = registryItemSchema.safeParse({ ...item, files, - }) + }); if (payload.success) { // Write to styles subdirectory (existing behavior) await fs.writeFile( path.join(targetPath, `${item.name}.json`), JSON.stringify(payload.data, null, 2), - 'utf8' - ) + "utf8", + ); // Write to root registry path: public/r/{item-name}.json await fs.writeFile( path.join(REGISTRY_PATH, `${item.name}.json`), JSON.stringify(payload.data, null, 2), - 'utf8' - ) + "utf8", + ); } } } @@ -487,12 +487,12 @@ async function buildStyles(registry: Registry) { // ---------------------------------------------------------------------------- // Build registry/styles/index.json. // ---------------------------------------------------------------------------- - const stylesJson = JSON.stringify(styles, null, 2) + const stylesJson = JSON.stringify(styles, null, 2); await fs.writeFile( - path.join(REGISTRY_PATH, 'styles/index.json'), + path.join(REGISTRY_PATH, "styles/index.json"), stylesJson, - 'utf8' - ) + "utf8", + ); } // ---------------------------------------------------------------------------- @@ -500,13 +500,13 @@ async function buildStyles(registry: Registry) { // ---------------------------------------------------------------------------- async function buildStylesIndex() { for (const style of styles) { - const targetPath = path.join(REGISTRY_PATH, 'styles', style.name) + const targetPath = path.join(REGISTRY_PATH, "styles", style.name); const dependencies = [ - 'tailwindcss-animate', - 'class-variance-authority', - 'lucide-react', - ] + "tailwindcss-animate", + "class-variance-authority", + "lucide-react", + ]; // TODO: Remove this when we migrate to lucide-react. // if (style.name === "new-york") { @@ -515,9 +515,9 @@ async function buildStylesIndex() { const payload: RegistryItem = { name: style.name, - type: 'registry:style', + type: "registry:style", dependencies, - registryDependencies: ['utils'], + registryDependencies: ["utils"], tailwind: { config: { plugins: [`require("tailwindcss-animate")`], @@ -525,13 +525,13 @@ async function buildStylesIndex() { }, cssVars: {}, files: [], - } + }; await fs.writeFile( - path.join(targetPath, 'index.json'), + path.join(targetPath, "index.json"), JSON.stringify(payload, null, 2), - 'utf8' - ) + "utf8", + ); } } @@ -539,49 +539,49 @@ async function buildStylesIndex() { // Build registry/colors/index.json. // ---------------------------------------------------------------------------- async function buildThemes() { - const colorsTargetPath = path.join(REGISTRY_PATH, 'colors') - rimraf.sync(colorsTargetPath) + const colorsTargetPath = path.join(REGISTRY_PATH, "colors"); + rimraf.sync(colorsTargetPath); if (!existsSync(colorsTargetPath)) { - await fs.mkdir(colorsTargetPath, { recursive: true }) + await fs.mkdir(colorsTargetPath, { recursive: true }); } // biome-ignore lint/suspicious/noExplicitAny: color data structure varies by color type - const colorsData: Record = {} + const colorsData: Record = {}; for (const [color, value] of Object.entries(colors)) { - if (typeof value === 'string') { - colorsData[color] = value - continue + if (typeof value === "string") { + colorsData[color] = value; + continue; } if (Array.isArray(value)) { colorsData[color] = value.map((item) => ({ ...item, - rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'), + rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, "$1 $2 $3"), hslChannel: item.hsl.replace( /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/, - '$1 $2 $3' + "$1 $2 $3", ), - })) - continue + })); + continue; } - if (typeof value === 'object') { + if (typeof value === "object") { colorsData[color] = { ...value, - rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'), + rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, "$1 $2 $3"), hslChannel: value.hsl.replace( /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/, - '$1 $2 $3' + "$1 $2 $3", ), - } + }; } } await fs.writeFile( - path.join(colorsTargetPath, 'index.json'), + path.join(colorsTargetPath, "index.json"), JSON.stringify(colorsData, null, 2), - 'utf8' - ) + "utf8", + ); // ---------------------------------------------------------------------------- // Build registry/colors/[base].json. @@ -589,7 +589,7 @@ async function buildThemes() { const BASE_STYLES = `@tailwind base; @tailwind components; @tailwind utilities; - ` + `; const BASE_STYLES_WITH_VARIABLES = `@tailwind base; @tailwind components; @@ -659,53 +659,53 @@ async function buildThemes() { body { @apply bg-background text-foreground; } -}` +}`; - for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) { + for (const baseColor of ["slate", "gray", "zinc", "neutral", "stone"]) { // biome-ignore lint/suspicious/noExplicitAny: color data structure varies by color type const base: Record = { inlineColors: {}, cssVars: {}, - } + }; for (const [mode, values] of Object.entries(colorMapping)) { - base.inlineColors[mode] = {} - base.cssVars[mode] = {} + base.inlineColors[mode] = {}; + base.cssVars[mode] = {}; for (const [key, value] of Object.entries(values)) { - if (typeof value === 'string') { + if (typeof value === "string") { // Chart colors do not have a 1-to-1 mapping with tailwind colors. - if (key.startsWith('chart-')) { - base.cssVars[mode][key] = value - continue + if (key.startsWith("chart-")) { + base.cssVars[mode][key] = value; + continue; } - const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`) - base.inlineColors[mode][key] = resolvedColor + const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`); + base.inlineColors[mode][key] = resolvedColor; - const [resolvedBase, scale] = resolvedColor.split('-') + const [resolvedBase, scale] = resolvedColor.split("-"); const color = scale ? colorsData[resolvedBase].find( // biome-ignore lint/suspicious/noExplicitAny: color items have dynamic structure - (item: any) => item.scale === Number.parseInt(scale, 10) + (item: any) => item.scale === Number.parseInt(scale, 10), ) - : colorsData[resolvedBase] + : colorsData[resolvedBase]; if (color) { - base.cssVars[mode][key] = color.hslChannel + base.cssVars[mode][key] = color.hslChannel; } } } } // Build css vars. - base.inlineColorsTemplate = createTemplate(BASE_STYLES)({}) + base.inlineColorsTemplate = createTemplate(BASE_STYLES)({}); base.cssVarsTemplate = createTemplate(BASE_STYLES_WITH_VARIABLES)({ colors: base.cssVars, - }) + }); await fs.writeFile( path.join(REGISTRY_PATH, `colors/${baseColor}.json`), JSON.stringify(base, null, 2), - 'utf8' - ) + "utf8", + ); // ---------------------------------------------------------------------------- // Build registry/themes.css @@ -773,88 +773,88 @@ async function buildThemes() { --destructive-foreground: <%- colors.dark["destructive-foreground"] %>; --ring: <%- colors.dark["ring"] %>; -}` +}`; - const themeCSS = [] + const themeCSS = []; for (const theme of baseColors) { themeCSS.push( // @ts-expect-error createTemplate(THEME_STYLES_WITH_VARIABLES)({ colors: theme.cssVars, theme: theme.name, - }) - ) + }), + ); } await fs.writeFile( - path.join(REGISTRY_PATH, 'themes.css'), - themeCSS.join('\n'), - 'utf8' - ) + path.join(REGISTRY_PATH, "themes.css"), + themeCSS.join("\n"), + "utf8", + ); // ---------------------------------------------------------------------------- // Build registry/themes/[theme].json // ---------------------------------------------------------------------------- - rimraf.sync(path.join(REGISTRY_PATH, 'themes')) - for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) { + rimraf.sync(path.join(REGISTRY_PATH, "themes")); + for (const baseColor of ["slate", "gray", "zinc", "neutral", "stone"]) { // biome-ignore lint/suspicious/noExplicitAny: theme payload structure varies by theme type const payload: Record = { name: baseColor, label: baseColor.charAt(0).toUpperCase() + baseColor.slice(1), cssVars: {}, - } + }; for (const [mode, values] of Object.entries(colorMapping)) { - payload.cssVars[mode] = {} + payload.cssVars[mode] = {}; for (const [key, value] of Object.entries(values)) { - if (typeof value === 'string') { - const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`) - payload.cssVars[mode][key] = resolvedColor + if (typeof value === "string") { + const resolvedColor = value.replace(/{{base}}-/g, `${baseColor}-`); + payload.cssVars[mode][key] = resolvedColor; - const [resolvedBase, scale] = resolvedColor.split('-') + const [resolvedBase, scale] = resolvedColor.split("-"); const color = scale ? colorsData[resolvedBase].find( // biome-ignore lint/suspicious/noExplicitAny: color items have dynamic structure - (item: any) => item.scale === Number.parseInt(scale, 10) + (item: any) => item.scale === Number.parseInt(scale, 10), ) - : colorsData[resolvedBase] + : colorsData[resolvedBase]; if (color) { - payload.cssVars[mode][key] = color.hslChannel + payload.cssVars[mode][key] = color.hslChannel; } } } } - const targetPath = path.join(REGISTRY_PATH, 'themes') + const targetPath = path.join(REGISTRY_PATH, "themes"); // Create directory if it doesn't exist. if (!existsSync(targetPath)) { - await fs.mkdir(targetPath, { recursive: true }) + await fs.mkdir(targetPath, { recursive: true }); } await fs.writeFile( path.join(targetPath, `${payload.name}.json`), JSON.stringify(payload, null, 2), - 'utf8' - ) + "utf8", + ); } } } try { - const result = registrySchema.safeParse(registry) + const result = registrySchema.safeParse(registry); if (!result.success) { - console.error(result.error) - process.exit(1) + console.error(result.error); + process.exit(1); } - await buildRegistry(result.data) - await buildStyles(result.data) - await buildStylesIndex() - await buildThemes() + await buildRegistry(result.data); + await buildStyles(result.data); + await buildStylesIndex(); + await buildThemes(); - console.log('✅ Done!') + console.log("✅ Done!"); } catch (error) { - console.error(error) - process.exit(1) + console.error(error); + process.exit(1); }