import type { Config } from '@/src/utils/get-config' import type { registryItemWithContentSchema } from '@/src/utils/registry/schema' import type * as z from 'zod' import process from 'node:process' import { registryBaseColorSchema, registryIndexSchema, registryWithContentSchema, stylesSchema, } from '@/src/utils/registry/schema' import consola from 'consola' import { ofetch } from 'ofetch' import path from 'pathe' import { ProxyAgent } from 'undici' const baseUrl = process.env.COMPONENTS_REGISTRY_URL ?? 'https://www.shadcn-vue.com' const agent = process.env.https_proxy ? new ProxyAgent(process.env.https_proxy) : undefined export async function getRegistryIndex() { try { const [result] = await fetchRegistry(['index.json']) return registryIndexSchema.parse(result) } catch (error) { throw new Error('Failed to fetch components from registry.') } } export async function getRegistryStyles() { try { const [result] = await fetchRegistry(['styles/index.json']) return stylesSchema.parse(result) } catch (error) { throw new Error('Failed to fetch styles from registry.') } } export function getRegistryBaseColors() { return [ { name: 'slate', label: 'Slate', }, { name: 'gray', label: 'Gray', }, { name: 'zinc', label: 'Zinc', }, { name: 'neutral', label: 'Neutral', }, { name: 'stone', label: 'Stone', }, ] } export async function getRegistryBaseColor(baseColor: string) { try { const [result] = await fetchRegistry([`colors/${baseColor}.json`]) return registryBaseColorSchema.parse(result) } catch (error) { throw new Error('Failed to fetch base color from registry.') } } export async function resolveTree( index: z.infer, names: string[], ) { const tree: z.infer = [] for (const name of names) { const entry = index.find(entry => entry.name === name) if (!entry) continue tree.push(entry) if (entry.registryDependencies) { const dependencies = await resolveTree(index, entry.registryDependencies) tree.push(...dependencies) } } return tree.filter( (component, index, self) => self.findIndex(c => c.name === component.name) === index, ) } export async function fetchTree( style: string, tree: z.infer, ) { try { const paths = tree.map(item => `styles/${style}/${item.name}.json`) const result = await fetchRegistry(paths) return registryWithContentSchema.parse(result) } catch (error) { throw new Error('Failed to fetch tree from registry.') } } export function getItemTargetPath( config: Config, item: Pick, 'type'>, override?: string, ) { // Allow overrides for all items but ui. if (override) return override if (item.type === 'components:ui' && config.aliases.ui) return config.resolvedPaths.ui const [parent, type] = item.type.split(':') if (!(parent in config.resolvedPaths)) return null return path.join( config.resolvedPaths[parent as keyof typeof config.resolvedPaths], type, ) } async function fetchRegistry(paths: string[]) { try { const results = await Promise.all( paths.map(async (path) => { const response = await ofetch(`${baseUrl}/registry/${path}`, { dispatcher: agent, }) return response }), ) return results } catch (error) { consola.error(error) throw new Error(`Failed to fetch registry from ${baseUrl}.`) } }