From 8d9be074a34fe7280b1bc9c0464a0d48720b66f7 Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 2 Feb 2026 14:31:40 +0400 Subject: [PATCH] feat: update migrate radix command --- apps/v4/content/docs/(root)/cli.mdx | 44 ++++++ .../r/styles/new-york-v4/accordion.json | 3 + .../r/styles/new-york-v4/alert-dialog.json | 3 + .../r/styles/new-york-v4/aspect-ratio.json | 3 + .../public/r/styles/new-york-v4/avatar.json | 3 + .../v4/public/r/styles/new-york-v4/badge.json | 3 + .../r/styles/new-york-v4/breadcrumb.json | 3 + .../public/r/styles/new-york-v4/button.json | 3 + .../public/r/styles/new-york-v4/checkbox.json | 3 + .../r/styles/new-york-v4/collapsible.json | 3 + .../r/styles/new-york-v4/context-menu.json | 3 + .../public/r/styles/new-york-v4/dialog.json | 3 + .../r/styles/new-york-v4/dropdown-menu.json | 3 + apps/v4/public/r/styles/new-york-v4/form.json | 1 + .../r/styles/new-york-v4/hover-card.json | 3 + apps/v4/public/r/styles/new-york-v4/item.json | 3 + .../v4/public/r/styles/new-york-v4/label.json | 3 + .../public/r/styles/new-york-v4/menubar.json | 3 + .../r/styles/new-york-v4/navigation-menu.json | 3 + .../public/r/styles/new-york-v4/popover.json | 3 + .../public/r/styles/new-york-v4/progress.json | 3 + .../r/styles/new-york-v4/radio-group.json | 3 + .../public/r/styles/new-york-v4/registry.json | 94 +++++++++++- .../r/styles/new-york-v4/resizable.json | 2 +- .../r/styles/new-york-v4/scroll-area.json | 3 + .../public/r/styles/new-york-v4/select.json | 3 + .../r/styles/new-york-v4/separator.json | 3 + .../v4/public/r/styles/new-york-v4/sheet.json | 3 + .../public/r/styles/new-york-v4/sidebar.json | 1 + .../public/r/styles/new-york-v4/slider.json | 3 + .../public/r/styles/new-york-v4/switch.json | 3 + apps/v4/public/r/styles/new-york-v4/tabs.json | 3 + .../r/styles/new-york-v4/toggle-group.json | 3 + .../public/r/styles/new-york-v4/toggle.json | 3 + .../public/r/styles/new-york-v4/tooltip.json | 3 + apps/v4/registry/new-york-v4/ui/_registry.ts | 37 ++++- packages/shadcn/src/commands/migrate.ts | 2 +- .../src/migrations/migrate-radix.test.ts | 20 ++- .../shadcn/src/migrations/migrate-radix.ts | 135 ++++++++++++------ 39 files changed, 374 insertions(+), 52 deletions(-) diff --git a/apps/v4/content/docs/(root)/cli.mdx b/apps/v4/content/docs/(root)/cli.mdx index 105bc58bd3..c425221541 100644 --- a/apps/v4/content/docs/(root)/cli.mdx +++ b/apps/v4/content/docs/(root)/cli.mdx @@ -289,3 +289,47 @@ npx shadcn@latest migrate rtl "src/components/ui/**" ``` If no path is provided, the migration will transform all files in your `ui` directory (from `components.json`). + +--- + +### migrate radix + +The `radix` migration updates your imports from individual `@radix-ui/react-*` packages to the unified `radix-ui` package. + +```bash +npx shadcn@latest migrate radix +``` + +This will: + +1. Transform imports from `@radix-ui/react-*` to `radix-ui` +2. Add the `radix-ui` package to your `package.json` + +**Before** + +```tsx +import * as DialogPrimitive from "@radix-ui/react-dialog" +import * as SelectPrimitive from "@radix-ui/react-select" +``` + +**After** + +```tsx +import { Dialog as DialogPrimitive, Select as SelectPrimitive } from "radix-ui" +``` + +**Migrate specific files** + +You can migrate specific files or use glob patterns: + +```bash +# Migrate a specific file. +npx shadcn@latest migrate radix src/components/ui/dialog.tsx + +# Migrate files matching a glob pattern. +npx shadcn@latest migrate radix "src/components/ui/**" +``` + +If no path is provided, the migration will transform all files in your `ui` directory (from `components.json`). + +Once complete, you can remove any unused `@radix-ui/react-*` packages from your `package.json`. diff --git a/apps/v4/public/r/styles/new-york-v4/accordion.json b/apps/v4/public/r/styles/new-york-v4/accordion.json index f525eb6009..6c35a6a1b3 100644 --- a/apps/v4/public/r/styles/new-york-v4/accordion.json +++ b/apps/v4/public/r/styles/new-york-v4/accordion.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "accordion", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/accordion.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/alert-dialog.json b/apps/v4/public/r/styles/new-york-v4/alert-dialog.json index 4727c98e06..35e333fe74 100644 --- a/apps/v4/public/r/styles/new-york-v4/alert-dialog.json +++ b/apps/v4/public/r/styles/new-york-v4/alert-dialog.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "alert-dialog", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "button" ], diff --git a/apps/v4/public/r/styles/new-york-v4/aspect-ratio.json b/apps/v4/public/r/styles/new-york-v4/aspect-ratio.json index 46916ebccd..fadd7bd4d8 100644 --- a/apps/v4/public/r/styles/new-york-v4/aspect-ratio.json +++ b/apps/v4/public/r/styles/new-york-v4/aspect-ratio.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "aspect-ratio", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/aspect-ratio.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/avatar.json b/apps/v4/public/r/styles/new-york-v4/avatar.json index a47f06e7d7..99b4accbff 100644 --- a/apps/v4/public/r/styles/new-york-v4/avatar.json +++ b/apps/v4/public/r/styles/new-york-v4/avatar.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "avatar", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/avatar.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/badge.json b/apps/v4/public/r/styles/new-york-v4/badge.json index 459e8d5ecd..c4bf061dbd 100644 --- a/apps/v4/public/r/styles/new-york-v4/badge.json +++ b/apps/v4/public/r/styles/new-york-v4/badge.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "badge", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/badge.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/breadcrumb.json b/apps/v4/public/r/styles/new-york-v4/breadcrumb.json index 2d8fe0c7e4..5c4849b467 100644 --- a/apps/v4/public/r/styles/new-york-v4/breadcrumb.json +++ b/apps/v4/public/r/styles/new-york-v4/breadcrumb.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "breadcrumb", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/breadcrumb.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/button.json b/apps/v4/public/r/styles/new-york-v4/button.json index 3a098ab082..5ea7a0c3f5 100644 --- a/apps/v4/public/r/styles/new-york-v4/button.json +++ b/apps/v4/public/r/styles/new-york-v4/button.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "button", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/button.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/checkbox.json b/apps/v4/public/r/styles/new-york-v4/checkbox.json index 653db8bb2c..428b88f9b6 100644 --- a/apps/v4/public/r/styles/new-york-v4/checkbox.json +++ b/apps/v4/public/r/styles/new-york-v4/checkbox.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "checkbox", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/checkbox.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/collapsible.json b/apps/v4/public/r/styles/new-york-v4/collapsible.json index a94ca6f5cb..0a0c082c80 100644 --- a/apps/v4/public/r/styles/new-york-v4/collapsible.json +++ b/apps/v4/public/r/styles/new-york-v4/collapsible.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "collapsible", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/collapsible.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/context-menu.json b/apps/v4/public/r/styles/new-york-v4/context-menu.json index 50c90f4ce7..06f1107afe 100644 --- a/apps/v4/public/r/styles/new-york-v4/context-menu.json +++ b/apps/v4/public/r/styles/new-york-v4/context-menu.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "context-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/context-menu.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/dialog.json b/apps/v4/public/r/styles/new-york-v4/dialog.json index 998609bdad..3d463eec4c 100644 --- a/apps/v4/public/r/styles/new-york-v4/dialog.json +++ b/apps/v4/public/r/styles/new-york-v4/dialog.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dialog", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/dialog.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/dropdown-menu.json b/apps/v4/public/r/styles/new-york-v4/dropdown-menu.json index eae4419416..4244cb60a3 100644 --- a/apps/v4/public/r/styles/new-york-v4/dropdown-menu.json +++ b/apps/v4/public/r/styles/new-york-v4/dropdown-menu.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "dropdown-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/dropdown-menu.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/form.json b/apps/v4/public/r/styles/new-york-v4/form.json index 4b9855f3c5..a61e51eaab 100644 --- a/apps/v4/public/r/styles/new-york-v4/form.json +++ b/apps/v4/public/r/styles/new-york-v4/form.json @@ -2,6 +2,7 @@ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "form", "dependencies": [ + "radix-ui", "@hookform/resolvers", "zod", "react-hook-form" diff --git a/apps/v4/public/r/styles/new-york-v4/hover-card.json b/apps/v4/public/r/styles/new-york-v4/hover-card.json index 065f8495f1..55773d4fb4 100644 --- a/apps/v4/public/r/styles/new-york-v4/hover-card.json +++ b/apps/v4/public/r/styles/new-york-v4/hover-card.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "hover-card", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/hover-card.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/item.json b/apps/v4/public/r/styles/new-york-v4/item.json index a4dd46e789..7354f06ead 100644 --- a/apps/v4/public/r/styles/new-york-v4/item.json +++ b/apps/v4/public/r/styles/new-york-v4/item.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "item", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "separator" ], diff --git a/apps/v4/public/r/styles/new-york-v4/label.json b/apps/v4/public/r/styles/new-york-v4/label.json index 1cfc7412b7..e8bb2b130a 100644 --- a/apps/v4/public/r/styles/new-york-v4/label.json +++ b/apps/v4/public/r/styles/new-york-v4/label.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "label", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/label.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/menubar.json b/apps/v4/public/r/styles/new-york-v4/menubar.json index 35499a9269..4357954a61 100644 --- a/apps/v4/public/r/styles/new-york-v4/menubar.json +++ b/apps/v4/public/r/styles/new-york-v4/menubar.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "menubar", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/menubar.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/navigation-menu.json b/apps/v4/public/r/styles/new-york-v4/navigation-menu.json index 601fe11b36..e1727976e8 100644 --- a/apps/v4/public/r/styles/new-york-v4/navigation-menu.json +++ b/apps/v4/public/r/styles/new-york-v4/navigation-menu.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "navigation-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/navigation-menu.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/popover.json b/apps/v4/public/r/styles/new-york-v4/popover.json index 576ad98687..5612761aca 100644 --- a/apps/v4/public/r/styles/new-york-v4/popover.json +++ b/apps/v4/public/r/styles/new-york-v4/popover.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "popover", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/popover.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/progress.json b/apps/v4/public/r/styles/new-york-v4/progress.json index 013da12cf0..070fe2376d 100644 --- a/apps/v4/public/r/styles/new-york-v4/progress.json +++ b/apps/v4/public/r/styles/new-york-v4/progress.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "progress", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/progress.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/radio-group.json b/apps/v4/public/r/styles/new-york-v4/radio-group.json index 0d132d87df..c32cdcbddf 100644 --- a/apps/v4/public/r/styles/new-york-v4/radio-group.json +++ b/apps/v4/public/r/styles/new-york-v4/radio-group.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "radio-group", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/radio-group.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/registry.json b/apps/v4/public/r/styles/new-york-v4/registry.json index 95d42b8c71..1ffea644d4 100644 --- a/apps/v4/public/r/styles/new-york-v4/registry.json +++ b/apps/v4/public/r/styles/new-york-v4/registry.json @@ -38,6 +38,9 @@ }, { "name": "accordion", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/accordion.tsx", @@ -58,6 +61,9 @@ }, { "name": "alert-dialog", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "button" ], @@ -71,6 +77,9 @@ }, { "name": "aspect-ratio", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/aspect-ratio.tsx", @@ -81,6 +90,9 @@ }, { "name": "avatar", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/avatar.tsx", @@ -91,6 +103,9 @@ }, { "name": "badge", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/badge.tsx", @@ -101,6 +116,9 @@ }, { "name": "breadcrumb", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/breadcrumb.tsx", @@ -111,6 +129,9 @@ }, { "name": "button", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/button.tsx", @@ -195,6 +216,9 @@ }, { "name": "checkbox", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/checkbox.tsx", @@ -205,6 +229,9 @@ }, { "name": "collapsible", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/collapsible.tsx", @@ -248,6 +275,9 @@ }, { "name": "context-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/context-menu.tsx", @@ -258,6 +288,9 @@ }, { "name": "dialog", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/dialog.tsx", @@ -281,6 +314,9 @@ }, { "name": "dropdown-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/dropdown-menu.tsx", @@ -316,6 +352,7 @@ { "name": "form", "dependencies": [ + "radix-ui", "@hookform/resolvers", "zod", "react-hook-form" @@ -334,6 +371,9 @@ }, { "name": "hover-card", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/hover-card.tsx", @@ -382,6 +422,9 @@ }, { "name": "item", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "separator" ], @@ -395,6 +438,9 @@ }, { "name": "label", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/label.tsx", @@ -405,6 +451,9 @@ }, { "name": "menubar", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/menubar.tsx", @@ -415,6 +464,9 @@ }, { "name": "navigation-menu", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/navigation-menu.tsx", @@ -438,6 +490,9 @@ }, { "name": "popover", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/popover.tsx", @@ -448,6 +503,9 @@ }, { "name": "progress", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/progress.tsx", @@ -458,6 +516,9 @@ }, { "name": "radio-group", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/radio-group.tsx", @@ -469,7 +530,7 @@ { "name": "resizable", "dependencies": [ - "react-resizable-panels" + "react-resizable-panels@^3" ], "files": [ { @@ -481,6 +542,9 @@ }, { "name": "scroll-area", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/scroll-area.tsx", @@ -491,6 +555,9 @@ }, { "name": "select", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/select.tsx", @@ -501,6 +568,9 @@ }, { "name": "separator", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/separator.tsx", @@ -511,6 +581,9 @@ }, { "name": "sheet", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/sheet.tsx", @@ -522,6 +595,7 @@ { "name": "sidebar", "dependencies": [ + "radix-ui", "class-variance-authority", "lucide-react" ], @@ -596,6 +670,9 @@ }, { "name": "slider", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/slider.tsx", @@ -633,6 +710,9 @@ }, { "name": "switch", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/switch.tsx", @@ -653,6 +733,9 @@ }, { "name": "tabs", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/tabs.tsx", @@ -673,6 +756,9 @@ }, { "name": "toggle", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/toggle.tsx", @@ -683,6 +769,9 @@ }, { "name": "toggle-group", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "toggle" ], @@ -696,6 +785,9 @@ }, { "name": "tooltip", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/tooltip.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/resizable.json b/apps/v4/public/r/styles/new-york-v4/resizable.json index 8a0ac3f293..a744bbb78e 100644 --- a/apps/v4/public/r/styles/new-york-v4/resizable.json +++ b/apps/v4/public/r/styles/new-york-v4/resizable.json @@ -2,7 +2,7 @@ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "resizable", "dependencies": [ - "react-resizable-panels" + "react-resizable-panels@^3" ], "files": [ { diff --git a/apps/v4/public/r/styles/new-york-v4/scroll-area.json b/apps/v4/public/r/styles/new-york-v4/scroll-area.json index 2e744039dc..f1d478eb1a 100644 --- a/apps/v4/public/r/styles/new-york-v4/scroll-area.json +++ b/apps/v4/public/r/styles/new-york-v4/scroll-area.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "scroll-area", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/scroll-area.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/select.json b/apps/v4/public/r/styles/new-york-v4/select.json index e1efe1293a..dc8726d023 100644 --- a/apps/v4/public/r/styles/new-york-v4/select.json +++ b/apps/v4/public/r/styles/new-york-v4/select.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "select", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/select.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/separator.json b/apps/v4/public/r/styles/new-york-v4/separator.json index d702ae0017..912d276a03 100644 --- a/apps/v4/public/r/styles/new-york-v4/separator.json +++ b/apps/v4/public/r/styles/new-york-v4/separator.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "separator", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/separator.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/sheet.json b/apps/v4/public/r/styles/new-york-v4/sheet.json index 1f41ab5858..0bc1d0026c 100644 --- a/apps/v4/public/r/styles/new-york-v4/sheet.json +++ b/apps/v4/public/r/styles/new-york-v4/sheet.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "sheet", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/sheet.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/sidebar.json b/apps/v4/public/r/styles/new-york-v4/sidebar.json index 6c43196fb4..10e78fe574 100644 --- a/apps/v4/public/r/styles/new-york-v4/sidebar.json +++ b/apps/v4/public/r/styles/new-york-v4/sidebar.json @@ -2,6 +2,7 @@ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "sidebar", "dependencies": [ + "radix-ui", "class-variance-authority", "lucide-react" ], diff --git a/apps/v4/public/r/styles/new-york-v4/slider.json b/apps/v4/public/r/styles/new-york-v4/slider.json index 48276478f3..de101a56dd 100644 --- a/apps/v4/public/r/styles/new-york-v4/slider.json +++ b/apps/v4/public/r/styles/new-york-v4/slider.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "slider", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/slider.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/switch.json b/apps/v4/public/r/styles/new-york-v4/switch.json index 061601fa42..6d99d98da8 100644 --- a/apps/v4/public/r/styles/new-york-v4/switch.json +++ b/apps/v4/public/r/styles/new-york-v4/switch.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "switch", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/switch.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/tabs.json b/apps/v4/public/r/styles/new-york-v4/tabs.json index db19dd52a9..094295d1e2 100644 --- a/apps/v4/public/r/styles/new-york-v4/tabs.json +++ b/apps/v4/public/r/styles/new-york-v4/tabs.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "tabs", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/tabs.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/toggle-group.json b/apps/v4/public/r/styles/new-york-v4/toggle-group.json index f06efb75a3..06264ece47 100644 --- a/apps/v4/public/r/styles/new-york-v4/toggle-group.json +++ b/apps/v4/public/r/styles/new-york-v4/toggle-group.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "toggle-group", + "dependencies": [ + "radix-ui" + ], "registryDependencies": [ "toggle" ], diff --git a/apps/v4/public/r/styles/new-york-v4/toggle.json b/apps/v4/public/r/styles/new-york-v4/toggle.json index 34a69ad3b4..c0bef02872 100644 --- a/apps/v4/public/r/styles/new-york-v4/toggle.json +++ b/apps/v4/public/r/styles/new-york-v4/toggle.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "toggle", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/toggle.tsx", diff --git a/apps/v4/public/r/styles/new-york-v4/tooltip.json b/apps/v4/public/r/styles/new-york-v4/tooltip.json index 87715c3366..d5c083248b 100644 --- a/apps/v4/public/r/styles/new-york-v4/tooltip.json +++ b/apps/v4/public/r/styles/new-york-v4/tooltip.json @@ -1,6 +1,9 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "tooltip", + "dependencies": [ + "radix-ui" + ], "files": [ { "path": "registry/new-york-v4/ui/tooltip.tsx", diff --git a/apps/v4/registry/new-york-v4/ui/_registry.ts b/apps/v4/registry/new-york-v4/ui/_registry.ts index 851e2c991f..c040e42b79 100644 --- a/apps/v4/registry/new-york-v4/ui/_registry.ts +++ b/apps/v4/registry/new-york-v4/ui/_registry.ts @@ -4,6 +4,7 @@ export const ui: Registry["items"] = [ { name: "accordion", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/accordion.tsx", @@ -24,6 +25,7 @@ export const ui: Registry["items"] = [ { name: "alert-dialog", type: "registry:ui", + dependencies: ["radix-ui"], registryDependencies: ["button"], files: [ { @@ -35,6 +37,7 @@ export const ui: Registry["items"] = [ { name: "aspect-ratio", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/aspect-ratio.tsx", @@ -45,6 +48,7 @@ export const ui: Registry["items"] = [ { name: "avatar", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/avatar.tsx", @@ -55,6 +59,7 @@ export const ui: Registry["items"] = [ { name: "badge", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/badge.tsx", @@ -65,6 +70,7 @@ export const ui: Registry["items"] = [ { name: "breadcrumb", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/breadcrumb.tsx", @@ -75,6 +81,7 @@ export const ui: Registry["items"] = [ { name: "button", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/button.tsx", @@ -142,6 +149,7 @@ export const ui: Registry["items"] = [ { name: "checkbox", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/checkbox.tsx", @@ -152,6 +160,7 @@ export const ui: Registry["items"] = [ { name: "collapsible", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/collapsible.tsx", @@ -186,6 +195,7 @@ export const ui: Registry["items"] = [ { name: "context-menu", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/context-menu.tsx", @@ -196,6 +206,7 @@ export const ui: Registry["items"] = [ { name: "dialog", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/dialog.tsx", @@ -217,6 +228,7 @@ export const ui: Registry["items"] = [ { name: "dropdown-menu", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/dropdown-menu.tsx", @@ -248,7 +260,7 @@ export const ui: Registry["items"] = [ { name: "form", type: "registry:ui", - dependencies: ["@hookform/resolvers", "zod", "react-hook-form"], + dependencies: ["radix-ui", "@hookform/resolvers", "zod", "react-hook-form"], registryDependencies: ["button", "label"], files: [ { @@ -260,6 +272,7 @@ export const ui: Registry["items"] = [ { name: "hover-card", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/hover-card.tsx", @@ -302,6 +315,7 @@ export const ui: Registry["items"] = [ { name: "item", type: "registry:ui", + dependencies: ["radix-ui"], registryDependencies: ["separator"], files: [ { @@ -313,6 +327,7 @@ export const ui: Registry["items"] = [ { name: "label", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/label.tsx", @@ -323,6 +338,7 @@ export const ui: Registry["items"] = [ { name: "menubar", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/menubar.tsx", @@ -333,6 +349,7 @@ export const ui: Registry["items"] = [ { name: "navigation-menu", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/navigation-menu.tsx", @@ -354,6 +371,7 @@ export const ui: Registry["items"] = [ { name: "popover", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/popover.tsx", @@ -364,6 +382,7 @@ export const ui: Registry["items"] = [ { name: "progress", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/progress.tsx", @@ -374,6 +393,7 @@ export const ui: Registry["items"] = [ { name: "radio-group", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/radio-group.tsx", @@ -384,7 +404,7 @@ export const ui: Registry["items"] = [ { name: "resizable", type: "registry:ui", - dependencies: ["react-resizable-panels"], + dependencies: ["react-resizable-panels@^3"], files: [ { path: "ui/resizable.tsx", @@ -395,6 +415,7 @@ export const ui: Registry["items"] = [ { name: "scroll-area", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/scroll-area.tsx", @@ -405,6 +426,7 @@ export const ui: Registry["items"] = [ { name: "select", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/select.tsx", @@ -415,6 +437,7 @@ export const ui: Registry["items"] = [ { name: "separator", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/separator.tsx", @@ -425,6 +448,7 @@ export const ui: Registry["items"] = [ { name: "sheet", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/sheet.tsx", @@ -435,7 +459,7 @@ export const ui: Registry["items"] = [ { name: "sidebar", type: "registry:ui", - dependencies: ["class-variance-authority", "lucide-react"], + dependencies: ["radix-ui", "class-variance-authority", "lucide-react"], registryDependencies: [ "button", "separator", @@ -507,6 +531,7 @@ export const ui: Registry["items"] = [ { name: "slider", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/slider.tsx", @@ -539,6 +564,7 @@ export const ui: Registry["items"] = [ { name: "switch", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/switch.tsx", @@ -559,6 +585,7 @@ export const ui: Registry["items"] = [ { name: "tabs", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/tabs.tsx", @@ -579,6 +606,7 @@ export const ui: Registry["items"] = [ { name: "toast", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/toast.tsx", @@ -597,6 +625,7 @@ export const ui: Registry["items"] = [ { name: "toggle", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/toggle.tsx", @@ -607,6 +636,7 @@ export const ui: Registry["items"] = [ { name: "toggle-group", type: "registry:ui", + dependencies: ["radix-ui"], registryDependencies: ["toggle"], files: [ { @@ -618,6 +648,7 @@ export const ui: Registry["items"] = [ { name: "tooltip", type: "registry:ui", + dependencies: ["radix-ui"], files: [ { path: "ui/tooltip.tsx", diff --git a/packages/shadcn/src/commands/migrate.ts b/packages/shadcn/src/commands/migrate.ts index 59006f9198..d218fc9636 100644 --- a/packages/shadcn/src/commands/migrate.ts +++ b/packages/shadcn/src/commands/migrate.ts @@ -100,7 +100,7 @@ export const migrate = new Command() } if (options.migration === "radix") { - await migrateRadix(config, { yes: options.yes }) + await migrateRadix(config, { yes: options.yes, path: options.path }) } if (options.migration === "rtl") { diff --git a/packages/shadcn/src/migrations/migrate-radix.test.ts b/packages/shadcn/src/migrations/migrate-radix.test.ts index 7a842a74e4..f560017a42 100644 --- a/packages/shadcn/src/migrations/migrate-radix.test.ts +++ b/packages/shadcn/src/migrations/migrate-radix.test.ts @@ -888,10 +888,13 @@ describe("migrateRadix - package.json updates", () => { }, } + // Old packages should remain - we don't remove them automatically. const expectedPackageJson = { name: "test-project", dependencies: { react: "^18.0.0", + "@radix-ui/react-dialog": "^1.0.0", + "@radix-ui/react-select": "^1.0.0", "other-package": "^1.0.0", "radix-ui": "latest", }, @@ -1030,23 +1033,26 @@ describe("migrateRadix - package.json updates", () => { ) }) - it("should only remove packages that were found in source files", async () => { + it("should not remove old @radix-ui packages from package.json", async () => { const mockPackageJson = { name: "test-project", dependencies: { react: "^18.0.0", "@radix-ui/react-dialog": "^1.0.0", "@radix-ui/react-select": "^1.0.0", - "@radix-ui/react-toast": "^1.0.0", // This one is NOT in source files + "@radix-ui/react-toast": "^1.0.0", "other-package": "^1.0.0", }, } + // Old packages should remain - we don't remove them automatically. const expectedPackageJson = { name: "test-project", dependencies: { react: "^18.0.0", - "@radix-ui/react-toast": "^1.0.0", // Should remain since not found in source + "@radix-ui/react-dialog": "^1.0.0", + "@radix-ui/react-select": "^1.0.0", + "@radix-ui/react-toast": "^1.0.0", "other-package": "^1.0.0", "radix-ui": "latest", }, @@ -1084,22 +1090,24 @@ describe("migrateRadix - package.json updates", () => { ) }) - it("should not remove @radix-ui/react-icons from package.json", async () => { + it("should keep @radix-ui/react-icons and other packages in package.json", async () => { const mockPackageJson = { name: "test-project", dependencies: { react: "^18.0.0", "@radix-ui/react-dialog": "^1.0.0", - "@radix-ui/react-icons": "^1.3.0", // Should NOT be removed + "@radix-ui/react-icons": "^1.3.0", "other-package": "^1.0.0", }, } + // Old packages should remain - we don't remove them automatically. const expectedPackageJson = { name: "test-project", dependencies: { react: "^18.0.0", - "@radix-ui/react-icons": "^1.3.0", // Should remain + "@radix-ui/react-dialog": "^1.0.0", + "@radix-ui/react-icons": "^1.3.0", "other-package": "^1.0.0", "radix-ui": "latest", }, diff --git a/packages/shadcn/src/migrations/migrate-radix.ts b/packages/shadcn/src/migrations/migrate-radix.ts index 6edff951d1..62051c135e 100644 --- a/packages/shadcn/src/migrations/migrate-radix.ts +++ b/packages/shadcn/src/migrations/migrate-radix.ts @@ -100,29 +100,78 @@ function processNamedImports( export async function migrateRadix( config: Config, - options: { yes?: boolean } = {} + options: { yes?: boolean; path?: string } = {} ) { - if (!config.resolvedPaths.ui) { - throw new Error( - "We could not find a valid `ui` path in your `components.json` file. Please ensure you have a valid `ui` path in your `components.json` file." - ) + // Determine files to migrate. + let files: string[] + let basePath: string + + if (options.path) { + // User provided a path/glob. + basePath = config.resolvedPaths.cwd + const isGlob = options.path.includes("*") + + if (isGlob) { + files = await fg(options.path, { + cwd: basePath, + onlyFiles: true, + ignore: ["**/node_modules/**"], + }) + } else { + const fullPath = path.resolve(basePath, options.path) + const stat = await fs.stat(fullPath).catch(() => null) + + if (!stat) { + throw new Error(`File not found: ${options.path}`) + } + + if (stat.isDirectory()) { + basePath = fullPath + files = await fg("**/*.{js,ts,jsx,tsx}", { + cwd: basePath, + onlyFiles: true, + ignore: ["**/node_modules/**"], + }) + } else if (stat.isFile()) { + files = [options.path] + } else { + throw new Error(`Unsupported path type: ${options.path}`) + } + } + + if (files.length === 0) { + throw new Error(`No files found matching: ${options.path}`) + } + } else { + // Default: use ui path from components.json. + if (!config.resolvedPaths.ui) { + throw new Error( + "We could not find a valid `ui` path in your `components.json` file. Please ensure you have a valid `ui` path in your `components.json` file." + ) + } + + basePath = config.resolvedPaths.ui + files = await fg("**/*.{js,ts,jsx,tsx}", { + cwd: basePath, + onlyFiles: true, + }) } - const uiPath = config.resolvedPaths.ui - const files = await fg("**/*.{js,ts,jsx,tsx}", { - cwd: uiPath, - }) - + // Confirm with user. if (!options.yes) { + const relativePath = options.path + ? options.path + : `./${path.relative(config.resolvedPaths.cwd, basePath)}` + const { confirm } = await prompts({ type: "confirm", name: "confirm", initial: true, message: `We will migrate ${highlighter.info( files.length - )} files in ${highlighter.info( - `./${path.relative(config.resolvedPaths.cwd, uiPath)}` - )} to ${highlighter.info("radix-ui")}. Continue?`, + )} file(s) in ${highlighter.info(relativePath)} to ${highlighter.info( + "radix-ui" + )}. Continue?`, }) if (!confirm) { @@ -138,7 +187,7 @@ export async function migrateRadix( files.map(async (file) => { migrationSpinner.text = `Migrating ${file}...` - const filePath = path.join(uiPath, file) + const filePath = path.join(basePath, file) const fileContent = await fs.readFile(filePath, "utf-8") const { content, replacedPackages } = await migrateRadixFile(fileContent) @@ -152,7 +201,7 @@ export async function migrateRadix( migrationSpinner.succeed("Migrating imports.") - // Update package.json dependencies + // Update package.json dependencies. const packageSpinner = spinner(`Updating package.json...`)?.start() try { @@ -168,38 +217,44 @@ export async function migrateRadix( const foundPackagesArray = Array.from(foundPackages) - // Remove packages from both dependencies and devDependencies if found in source files - const dependencyTypes = ["dependencies", "devDependencies"] as const - for (const depType of dependencyTypes) { - if (packageJson[depType]) { - for (const pkg of foundPackagesArray) { - if (packageJson[depType]![pkg]) { - delete packageJson[depType]![pkg] - } - } - } - } - // Add radix-ui if we found any Radix packages. if (foundPackagesArray.length > 0) { if (!packageJson.dependencies) { packageJson.dependencies = {} } - packageJson.dependencies["radix-ui"] = "latest" - const packageJsonPath = path.join( - config.resolvedPaths.cwd, - "package.json" + // Only add radix-ui if it's not already in dependencies. + const hasRadixUi = + packageJson.dependencies?.["radix-ui"] || + packageJson.devDependencies?.["radix-ui"] + + if (!hasRadixUi) { + packageJson.dependencies["radix-ui"] = "latest" + + const packageJsonPath = path.join( + config.resolvedPaths.cwd, + "package.json" + ) + await fs.writeFile( + packageJsonPath, + JSON.stringify(packageJson, null, 2) + "\n" + ) + + packageSpinner.succeed(`Updated package.json.`) + + // Install radix-ui dependency. + await updateDependencies(["radix-ui"], [], config, { silent: false }) + } else { + packageSpinner.succeed(`radix-ui already in package.json.`) + } + + // Show a message about old packages that can be removed. + logger.info("") + logger.info( + `Migration complete. The following packages may be removed if no longer in use:` ) - await fs.writeFile( - packageJsonPath, - JSON.stringify(packageJson, null, 2) + "\n" - ) - - packageSpinner.succeed(`Updated package.json.`) - - // Install radix-ui dependency. - await updateDependencies(["radix-ui"], [], config, { silent: false }) + logger.info(highlighter.info(foundPackagesArray.join(", "))) + logger.info(`Please review your codebase before removing.`) } else { packageSpinner.succeed("No packages found in source files.") }