Skip to content

Commit

Permalink
feat(shadcn): recursively resolve registry dependencies (shadcn-ui#4961)
Browse files Browse the repository at this point in the history
* feat(shadcn): recursively resolve registry dependencies

* chore: add changeset

* ci: update actions/upload-artifact
  • Loading branch information
shadcn authored Sep 25, 2024
1 parent bd54184 commit 28f34ed
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/hip-moose-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"shadcn": minor
---

recursively resolve registry dependencies
2 changes: 1 addition & 1 deletion .github/workflows/prerelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
path: packages/shadcn

- name: Upload packaged artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: npm-package-shadcn@${{ steps.package-version.outputs.current-version }}-pr-${{ github.event.number }} # encode the PR number into the artifact name
path: packages/shadcn/dist/index.js
85 changes: 57 additions & 28 deletions packages/shadcn/src/utils/registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,44 +265,33 @@ export async function registryResolveItemsTree(
return null
}

let items = (
await Promise.all(
names.map(async (name) => {
const item = await getRegistryItem(name, config.style)
return item
})
)
).filter((item): item is NonNullable<typeof item> => item !== null)

if (!items.length) {
return null
// If we're resolving the index, we want it to go first.
if (names.includes("index")) {
names.unshift("index")
}

const registryDependencies: string[] = items
.map((item) => item.registryDependencies ?? [])
.flat()
let registryDependencies: string[] = []
for (const name of names) {
const itemRegistryDependencies = await resolveRegistryDependencies(
name,
config
)
registryDependencies.push(...itemRegistryDependencies)
}

const uniqueDependencies = Array.from(new Set(registryDependencies))
const urls = Array.from([...names, ...uniqueDependencies]).map((name) =>
getRegistryUrl(isUrl(name) ? name : `styles/${config.style}/${name}.json`)
)
let result = await fetchRegistry(urls)
const uniqueRegistryDependencies = Array.from(new Set(registryDependencies))
let result = await fetchRegistry(uniqueRegistryDependencies)
const payload = z.array(registryItemSchema).parse(result)

if (!payload) {
return null
}

// If we're resolving the index, we want it to go first.
// If we're resolving the index, we want to fetch
// the theme item if a base color is provided.
// We do this for index only.
// Other components will ship with their theme tokens.
if (names.includes("index")) {
const index = await getRegistryItem("index", config.style)
if (index) {
payload.unshift(index)
}

// Fetch the theme item if a base color is provided.
// We do this for index only.
// Other components will ship with their theme tokens.
if (config.tailwind.baseColor) {
const theme = await registryGetTheme(config.tailwind.baseColor, config)
if (theme) {
Expand Down Expand Up @@ -346,6 +335,46 @@ export async function registryResolveItemsTree(
}
}

async function resolveRegistryDependencies(
url: string,
config: Config
): Promise<string[]> {
const visited = new Set<string>()
const payload: string[] = []

async function resolveDependencies(itemUrl: string) {
const url = getRegistryUrl(
isUrl(itemUrl) ? itemUrl : `styles/${config.style}/${itemUrl}.json`
)

if (visited.has(url)) {
return
}

visited.add(url)

try {
const [result] = await fetchRegistry([url])
const item = registryItemSchema.parse(result)
payload.push(url)

if (item.registryDependencies) {
for (const dependency of item.registryDependencies) {
await resolveDependencies(dependency)
}
}
} catch (error) {
console.error(
`Error fetching or parsing registry item at ${itemUrl}:`,
error
)
}
}

await resolveDependencies(url)
return Array.from(new Set(payload))
}

export async function registryGetTheme(name: string, config: Config) {
const baseColor = await getRegistryBaseColor(name)
if (!baseColor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ exports[`registryResolveItemTree > should resolve index 1`] = `
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
"tailwindcss-animate",
"class-variance-authority",
"lucide-react",
"@radix-ui/react-label",
"clsx",
"tailwind-merge",
"@radix-ui/react-label",
],
"devDependencies": [],
"docs": "",
"files": [
{
"content": "import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
",
"path": "lib/utils.ts",
"type": "registry:lib",
},
{
"content": ""use client"
Expand Down Expand Up @@ -54,23 +62,11 @@ export { Label }
"target": "",
"type": "registry:ui",
},
{
"content": "import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
",
"path": "lib/utils.ts",
"type": "registry:lib",
},
],
"tailwind": {
"config": {
"plugins": [
"require("tailwindcss-animate")",
"require("tailwindcss-animate")",
],
"theme": {
"extend": {
Expand Down

0 comments on commit 28f34ed

Please sign in to comment.