mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-22 20:25:44 +00:00
Compare commits
4 Commits
shadcn@3.4
...
shadcn@3.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed9d5939e6 | ||
|
|
b52ec12f1e | ||
|
|
2ab9bff4bb | ||
|
|
2f6b51fa0a |
@@ -49,7 +49,7 @@ import { Badge } from "@/components/ui/badge"
|
|||||||
```
|
```
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<Badge variant="default |outline | secondary | destructive">Badge</Badge>
|
<Badge variant="default | outline | secondary | destructive">Badge</Badge>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Link
|
### Link
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
"recharts": "2.15.1",
|
"recharts": "2.15.1",
|
||||||
"rehype-pretty-code": "^0.14.1",
|
"rehype-pretty-code": "^0.14.1",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"shadcn": "3.4.1",
|
"shadcn": "3.4.2",
|
||||||
"shiki": "^1.10.1",
|
"shiki": "^1.10.1",
|
||||||
"sonner": "^2.0.0",
|
"sonner": "^2.0.0",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"@bucharitesh": "https://bucharitesh.in/r/{name}.json",
|
"@bucharitesh": "https://bucharitesh.in/r/{name}.json",
|
||||||
"@clerk": "https://clerk.com/r/{name}.json",
|
"@clerk": "https://clerk.com/r/{name}.json",
|
||||||
"@cult-ui": "https://cult-ui.com/r/{name}.json",
|
"@cult-ui": "https://cult-ui.com/r/{name}.json",
|
||||||
|
"@efferd-ui": "https://ui.efferd.com/r/{name}.json",
|
||||||
"@eldoraui": "https://eldoraui.site/r/{name}.json",
|
"@eldoraui": "https://eldoraui.site/r/{name}.json",
|
||||||
"@elements": "https://tryelements.dev/r/{name}.json",
|
"@elements": "https://tryelements.dev/r/{name}.json",
|
||||||
"@elevenlabs-ui": "https://ui.elevenlabs.io/r/{name}.json",
|
"@elevenlabs-ui": "https://ui.elevenlabs.io/r/{name}.json",
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
"react-resizable-panels": "^2.0.22",
|
"react-resizable-panels": "^2.0.22",
|
||||||
"react-wrap-balancer": "^0.4.1",
|
"react-wrap-balancer": "^0.4.1",
|
||||||
"recharts": "2.12.7",
|
"recharts": "2.12.7",
|
||||||
"shadcn": "3.4.1",
|
"shadcn": "3.4.2",
|
||||||
"sharp": "^0.32.6",
|
"sharp": "^0.32.6",
|
||||||
"sonner": "^1.2.3",
|
"sonner": "^1.2.3",
|
||||||
"swr": "2.2.6-beta.3",
|
"swr": "2.2.6-beta.3",
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @shadcn/ui
|
# @shadcn/ui
|
||||||
|
|
||||||
|
## 3.4.2
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#8478](https://github.com/shadcn-ui/ui/pull/8478) [`b52ec12f1e22cf89270bf3d931f5b7544e4b80b4`](https://github.com/shadcn-ui/ui/commit/b52ec12f1e22cf89270bf3d931f5b7544e4b80b4) Thanks [@shadcn](https://github.com/shadcn)! - fix regression with universal item detection
|
||||||
|
|
||||||
## 3.4.1
|
## 3.4.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "shadcn",
|
"name": "shadcn",
|
||||||
"version": "3.4.1",
|
"version": "3.4.2",
|
||||||
"description": "Add components to your apps.",
|
"description": "Add components to your apps.",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ describe("isLocalFile", () => {
|
|||||||
describe("isUniversalRegistryItem", () => {
|
describe("isUniversalRegistryItem", () => {
|
||||||
it("should return true when all files have targets with registry:file type", () => {
|
it("should return true when all files have targets with registry:file type", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "file1.ts",
|
path: "file1.ts",
|
||||||
@@ -160,7 +161,26 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return true for any registry item type if all files are registry:file with targets", () => {
|
it("should return true when registry item type is registry:file and all files have targets", () => {
|
||||||
|
const registryItem = {
|
||||||
|
type: "registry:file" as const,
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
path: "file1.ts",
|
||||||
|
target: "src/file1.ts",
|
||||||
|
type: "registry:file" as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "file2.ts",
|
||||||
|
target: "src/utils/file2.ts",
|
||||||
|
type: "registry:item" as const,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return false for any registry item type other than registry:item or registry:file", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
type: "registry:ui" as const,
|
type: "registry:ui" as const,
|
||||||
files: [
|
files: [
|
||||||
@@ -171,11 +191,12 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return false when some files lack targets", () => {
|
it("should return false when some files lack targets", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "file1.ts",
|
path: "file1.ts",
|
||||||
@@ -190,6 +211,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when files have non-registry:file type", () => {
|
it("should return false when files have non-registry:file type", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "file1.ts",
|
path: "file1.ts",
|
||||||
@@ -208,6 +230,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when no files have targets", () => {
|
it("should return false when no files have targets", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{ path: "file1.ts", target: "", type: "registry:file" as const },
|
{ path: "file1.ts", target: "", type: "registry:file" as const },
|
||||||
{ path: "file2.ts", target: "", type: "registry:file" as const },
|
{ path: "file2.ts", target: "", type: "registry:file" as const },
|
||||||
@@ -216,18 +239,44 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return true when files array is empty", () => {
|
it("should return true when files array is empty and type is registry:item", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [],
|
files: [],
|
||||||
}
|
}
|
||||||
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return true when files is undefined", () => {
|
it("should return true when files is undefined and type is registry:item", () => {
|
||||||
const registryItem = {}
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
|
}
|
||||||
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should return false when type is registry:style", () => {
|
||||||
|
const registryItem = {
|
||||||
|
type: "registry:style" as const,
|
||||||
|
files: [],
|
||||||
|
}
|
||||||
|
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return false when type is registry:ui", () => {
|
||||||
|
const registryItem = {
|
||||||
|
type: "registry:ui" as const,
|
||||||
|
files: [],
|
||||||
|
}
|
||||||
|
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return false when files is undefined and type is not registry:item or registry:file", () => {
|
||||||
|
const registryItem = {
|
||||||
|
type: "registry:component" as const,
|
||||||
|
}
|
||||||
|
expect(isUniversalRegistryItem(registryItem)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
it("should return false when registryItem is null", () => {
|
it("should return false when registryItem is null", () => {
|
||||||
expect(isUniversalRegistryItem(null)).toBe(false)
|
expect(isUniversalRegistryItem(null)).toBe(false)
|
||||||
})
|
})
|
||||||
@@ -238,6 +287,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when target is null", () => {
|
it("should return false when target is null", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "file1.ts",
|
path: "file1.ts",
|
||||||
@@ -251,6 +301,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when target is undefined", () => {
|
it("should return false when target is undefined", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "file1.ts",
|
path: "file1.ts",
|
||||||
@@ -264,6 +315,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when files have registry:component type even with targets", () => {
|
it("should return false when files have registry:component type even with targets", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "component.tsx",
|
path: "component.tsx",
|
||||||
@@ -277,6 +329,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when files have registry:hook type even with targets", () => {
|
it("should return false when files have registry:hook type even with targets", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "use-hook.ts",
|
path: "use-hook.ts",
|
||||||
@@ -290,6 +343,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return false when files have registry:lib type even with targets", () => {
|
it("should return false when files have registry:lib type even with targets", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "utils.ts",
|
path: "utils.ts",
|
||||||
@@ -303,6 +357,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should return true when all targets are non-empty strings for registry:file", () => {
|
it("should return true when all targets are non-empty strings for registry:file", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{ path: "file1.ts", target: " ", type: "registry:file" as const }, // whitespace is truthy
|
{ path: "file1.ts", target: " ", type: "registry:file" as const }, // whitespace is truthy
|
||||||
{ path: "file2.ts", target: "0", type: "registry:file" as const }, // "0" is truthy
|
{ path: "file2.ts", target: "0", type: "registry:file" as const }, // "0" is truthy
|
||||||
@@ -313,6 +368,7 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
|
|
||||||
it("should handle real-world example with path traversal attempts for registry:file", () => {
|
it("should handle real-world example with path traversal attempts for registry:file", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
|
type: "registry:item" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "malicious.ts",
|
path: "malicious.ts",
|
||||||
@@ -326,18 +382,18 @@ describe("isUniversalRegistryItem", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
// The function should still return true - path validation is handled elsewhere
|
// The function should still return true - path validation is handled elsewhere.
|
||||||
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
expect(isUniversalRegistryItem(registryItem)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return false when files have non-registry:file type in a UI registry item", () => {
|
it("should return false when registry item type is registry:ui", () => {
|
||||||
const registryItem = {
|
const registryItem = {
|
||||||
type: "registry:ui" as const,
|
type: "registry:ui" as const,
|
||||||
files: [
|
files: [
|
||||||
{
|
{
|
||||||
path: "button.tsx",
|
path: "button.tsx",
|
||||||
target: "src/components/ui/button.tsx",
|
target: "src/components/ui/button.tsx",
|
||||||
type: "registry:ui" as const, // Not registry:file
|
type: "registry:file" as const,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,14 +267,14 @@ export function isLocalFile(path: string) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a registry item is universal (framework-agnostic).
|
* Check if a registry item is universal (framework-agnostic).
|
||||||
* A universal registry item must have all files with:
|
* A universal registry item must:
|
||||||
* 1. Explicit targets
|
* 1. Have type "registry:item" or "registry:file"
|
||||||
* 2. Type "registry:file"
|
* 2. If it has files, all files must have explicit targets and be type "registry:file" or "registry:item"
|
||||||
* It can be installed without framework detection or components.json.
|
* It can be installed without framework detection or components.json.
|
||||||
*/
|
*/
|
||||||
export function isUniversalRegistryItem(
|
export function isUniversalRegistryItem(
|
||||||
registryItem:
|
registryItem:
|
||||||
| Pick<z.infer<typeof registryItemSchema>, "files">
|
| Pick<z.infer<typeof registryItemSchema>, "files" | "type">
|
||||||
| null
|
| null
|
||||||
| undefined
|
| undefined
|
||||||
): boolean {
|
): boolean {
|
||||||
@@ -282,8 +282,16 @@ export function isUniversalRegistryItem(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
registryItem.type !== "registry:item" &&
|
||||||
|
registryItem.type !== "registry:file"
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const files = registryItem.files ?? []
|
const files = registryItem.files ?? []
|
||||||
|
|
||||||
|
// If there are files, all must have targets and be of type registry:file or registry:item.
|
||||||
return files.every(
|
return files.every(
|
||||||
(file) =>
|
(file) =>
|
||||||
!!file.target &&
|
!!file.target &&
|
||||||
|
|||||||
4
pnpm-lock.yaml
generated
4
pnpm-lock.yaml
generated
@@ -331,7 +331,7 @@ importers:
|
|||||||
specifier: ^6.0.1
|
specifier: ^6.0.1
|
||||||
version: 6.0.1
|
version: 6.0.1
|
||||||
shadcn:
|
shadcn:
|
||||||
specifier: 3.4.1
|
specifier: 3.4.2
|
||||||
version: link:../../packages/shadcn
|
version: link:../../packages/shadcn
|
||||||
shiki:
|
shiki:
|
||||||
specifier: ^1.10.1
|
specifier: ^1.10.1
|
||||||
@@ -611,7 +611,7 @@ importers:
|
|||||||
specifier: 2.12.7
|
specifier: 2.12.7
|
||||||
version: 2.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 2.12.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
shadcn:
|
shadcn:
|
||||||
specifier: 3.4.1
|
specifier: 3.4.2
|
||||||
version: link:../../packages/shadcn
|
version: link:../../packages/shadcn
|
||||||
sharp:
|
sharp:
|
||||||
specifier: ^0.32.6
|
specifier: ^0.32.6
|
||||||
|
|||||||
Reference in New Issue
Block a user