feat: update migrate radix command

This commit is contained in:
shadcn
2026-02-02 14:31:40 +04:00
parent 540cd031c3
commit 8d9be074a3
39 changed files with 374 additions and 52 deletions

View File

@@ -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`.

View File

@@ -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",

View File

@@ -1,6 +1,9 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "alert-dialog",
"dependencies": [
"radix-ui"
],
"registryDependencies": [
"button"
],

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -2,6 +2,7 @@
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form",
"dependencies": [
"radix-ui",
"@hookform/resolvers",
"zod",
"react-hook-form"

View File

@@ -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",

View File

@@ -1,6 +1,9 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "item",
"dependencies": [
"radix-ui"
],
"registryDependencies": [
"separator"
],

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -2,7 +2,7 @@
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "resizable",
"dependencies": [
"react-resizable-panels"
"react-resizable-panels@^3"
],
"files": [
{

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -2,6 +2,7 @@
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "sidebar",
"dependencies": [
"radix-ui",
"class-variance-authority",
"lucide-react"
],

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -1,6 +1,9 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "toggle-group",
"dependencies": [
"radix-ui"
],
"registryDependencies": [
"toggle"
],

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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") {

View File

@@ -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",
},

View File

@@ -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.")
}