import { z } from "zod"
// Note: if you edit the schema here, you must also edit the schema in the
// apps/www/public/schema/registry-item.json file.
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()])
type Literal = z.infer<typeof literalSchema>
type Json = Literal | { [key: string]: Json } | Json[]
const jsonSchema: z.ZodType<Json> = z.lazy(() =>
z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)])
)
export const registryItemTypeSchema = z.enum([
"registry:lib",
"registry:block",
"registry:component",
"registry:ui",
"registry:hook",
"registry:page",
"registry:file",
"registry:theme",
"registry:style",
// Internal use only
"registry:example",
"registry:internal",
])
export const registryItemFileSchema = z.discriminatedUnion("type", [
// Target is required for registry:file and registry:page
z.object({
path: z.string(),
content: z.string().optional(),
type: z.enum(["registry:file", "registry:page"]),
target: z.string(),
}),
z.object({
path: z.string(),
content: z.string().optional(),
type: registryItemTypeSchema.exclude(["registry:file", "registry:page"]),
target: z.string().optional(),
}),
])
export const tailwindConfigSchema = z
.object({
content: z.array(z.string()).optional(),
theme: z
.object({
extend: z
.object({
keyframes: z.record(z.string(), z.unknown()).optional(),
animation: z.record(z.string(), z.unknown()).optional(),
borderRadius: z.record(z.string(), z.string()).optional(),
colors: z.record(z.string(), z.unknown()).optional(),
})
.optional(),
})
.optional(),
plugins: z.array(z.string()).optional(),
})
.optional()
export const registryItemTailwindSchema = z.object({
config: tailwindConfigSchema,
})
export const registryItemCssVarsSchema = z.object({
theme: z.record(z.string(), z.string()).optional(),
light: z.record(z.string(), z.string()).optional(),
dark: z.record(z.string(), z.string()).optional(),
})
export const registryItemCssSchema = z.record(
z.string(),
z.union([
z.string(),
z.record(
z.string(),
z.union([z.string(), z.record(z.string(), z.string())])
),
])
)
export const registryItemSchema = z.object({
$schema: z.string().optional(),
extends: z.string().optional(),
name: z.string(),
type: registryItemTypeSchema,
title: z.string().optional(),
author: z.string().min(2).optional(),
description: z.string().optional(),
dependencies: z.array(z.string()).optional(),
devDependencies: z.array(z.string()).optional(),
registryDependencies: z.array(z.string()).optional(),
files: z.array(registryItemFileSchema).optional(),
tailwind: registryItemTailwindSchema.optional(),
cssVars: registryItemCssVarsSchema.optional(),
css: registryItemCssSchema.optional(),
meta: z.record(z.string(), z.unknown()).optional(),
docs: z.string().optional(),
categories: z.array(z.string()).optional(),
})
export type RegistryItem = z.infer<typeof registryItemSchema>
export const registrySchema = z.object({
name: z.string(),
homepage: z.string(),
items: z.array(registryItemSchema),
})
export type Registry = z.infer<typeof registrySchema>
export const registryIndexSchema = z.array(registryItemSchema)
export const stylesSchema = z.array(
z.object({
name: z.string(),
label: z.string(),
})
)
export const iconsSchema = z.record(
z.string(),
z.record(z.string(), z.string())
)
export const registryBaseColorSchema = z.object({
inlineColors: z.object({
light: z.record(z.string(), z.string()),
dark: z.record(z.string(), z.string()),
}),
cssVars: registryItemCssVarsSchema,
cssVarsV4: registryItemCssVarsSchema.optional(),
inlineColorsTemplate: z.string(),
cssVarsTemplate: z.string(),
})
export const registryResolvedItemsTreeSchema = registryItemSchema.pick({
dependencies: true,
devDependencies: true,
files: true,
tailwind: true,
cssVars: true,
css: true,
docs: true,
})