UNPKG

shadcn-vue

Version:
1,254 lines (1,240 loc) 37.4 kB
// src/registry/schema.ts import { z } from "zod"; var 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" ]); var 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() }) ]); var registryItemTailwindSchema = z.object({ config: z.object({ content: z.array(z.string()).optional(), theme: z.record(z.string(), z.any()).optional(), plugins: z.array(z.string()).optional() }).optional() }); var 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() }); var registryItemCssSchema = z.record( z.string(), z.lazy( () => z.union([ z.string(), z.record( z.string(), z.union([z.string(), z.record(z.string(), z.string())]) ) ]) ) ); var 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.any()).optional(), docs: z.string().optional(), categories: z.array(z.string()).optional() }); var registrySchema = z.object({ name: z.string(), homepage: z.string(), items: z.array(registryItemSchema) }); var registryIndexSchema = z.array(registryItemSchema); var stylesSchema = z.array( z.object({ name: z.string(), label: z.string() }) ); var iconsSchema = z.record( z.string(), z.record(z.string(), z.string()) ); var 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() }); var registryResolvedItemsTreeSchema = registryItemSchema.pick({ dependencies: true, devDependencies: true, files: true, tailwind: true, cssVars: true, css: true, docs: true }); // src/utils/frameworks.ts var FRAMEWORKS = { vite: { name: "vite", label: "Vite", links: { installation: "https://shadcn-vue.com/docs/installation/vite", tailwind: "https://tailwindcss.com/docs/guides/vite" } }, nuxt: { name: "nuxt", label: "Nuxt", links: { installation: "https://shadcn-vue.com/docs/installation/nuxt", tailwind: "https://tailwindcss.com/docs/guides/nuxtjs" } }, astro: { name: "astro", label: "Astro", links: { installation: "https://shadcn-vue.com/docs/installation/astro", tailwind: "https://tailwindcss.com/docs/guides/astro" } }, laravel: { name: "laravel", label: "Laravel", links: { installation: "https://shadcn-vue.com/docs/installation/laravel", tailwind: "https://tailwindcss.com/docs/guides/laravel" } }, manual: { name: "manual", label: "Manual", links: { installation: "https://shadcn-vue.com/docs/installation/manual", tailwind: "https://tailwindcss.com/docs/installation" } } }; // src/utils/get-package-info.ts import fs from "fs-extra"; import path from "pathe"; function getPackageInfo(cwd = "", shouldThrow = true) { const packageJsonPath = path.join(cwd, "package.json"); return fs.readJSONSync(packageJsonPath, { throws: shouldThrow }); } // src/utils/get-project-info.ts import fs2 from "fs-extra"; import { parseTsconfig } from "get-tsconfig"; import path2 from "pathe"; import { glob } from "tinyglobby"; import { z as z2 } from "zod"; var PROJECT_SHARED_IGNORE = [ "**/node_modules/**", ".nuxt", "public", "dist", "build" ]; var TS_CONFIG_SCHEMA = z2.object({ compilerOptions: z2.object({ paths: z2.record(z2.string().or(z2.array(z2.string()))) }) }); async function getProjectInfo(cwd) { const [ configFiles, typescript, tailwindConfigFile, tailwindCssFile, tailwindVersion, aliasPrefix, packageJson ] = await Promise.all([ glob("**/{nuxt,vite,astro}.config.*|composer.json", { cwd, deep: 3, ignore: PROJECT_SHARED_IGNORE }), isTypeScriptProject(cwd), getTailwindConfigFile(cwd), getTailwindCssFile(cwd), getTailwindVersion(cwd), getTsConfigAliasPrefix(cwd), getPackageInfo(cwd, false) ]); const type = { framework: FRAMEWORKS.manual, typescript, tailwindConfigFile, tailwindCssFile, tailwindVersion, aliasPrefix }; if (configFiles.find((file) => file.startsWith("nuxt.config."))?.length) { type.framework = FRAMEWORKS.nuxt; return type; } if (configFiles.find((file) => file.startsWith("astro.config."))?.length) { type.framework = FRAMEWORKS.astro; return type; } if (configFiles.find((file) => file.startsWith("composer.json"))?.length) { type.framework = FRAMEWORKS.laravel; return type; } if (configFiles.find((file) => file.startsWith("vite.config."))?.length) { type.framework = FRAMEWORKS.vite; return type; } return type; } async function getTailwindVersion(cwd) { const [packageInfo, config] = await Promise.all([ getPackageInfo(cwd), getConfig(cwd) ]); if (config?.tailwind?.config === "") { return "v4"; } if (!packageInfo?.dependencies?.tailwindcss && !packageInfo?.devDependencies?.tailwindcss) { return null; } if (/^(?:\^|~)?3(?:\.\d+)*(?:-.*)?$/.test( packageInfo?.dependencies?.tailwindcss || packageInfo?.devDependencies?.tailwindcss || "" )) { return "v3"; } return "v4"; } async function getTailwindCssFile(cwd) { const [files, tailwindVersion] = await Promise.all([ glob(["**/*.css", "**/*.scss"], { cwd, deep: 5, ignore: PROJECT_SHARED_IGNORE }), getTailwindVersion(cwd) ]); if (!files.length) { return null; } const needle = tailwindVersion === "v4" ? `@import "tailwindcss"` : "@tailwind base"; for (const file of files) { const contents = await fs2.readFile(path2.resolve(cwd, file), "utf8"); if (contents.includes(`@import "tailwindcss"`) || contents.includes(`@import 'tailwindcss'`) || contents.includes(`@tailwind base`)) { return file; } } return null; } async function getTailwindConfigFile(cwd) { const files = await glob("tailwind.config.*", { cwd, deep: 3, ignore: PROJECT_SHARED_IGNORE }); if (!files.length) { return null; } return files[0]; } async function getTsConfigAliasPrefix(cwd) { const isTypescript = await isTypeScriptProject(cwd); const tsconfigType = isTypescript ? "tsconfig.json" : "jsconfig.json"; const tsConfig = getTSConfig(cwd, tsconfigType); const parsedTsConfig = parseTsconfig(tsConfig.path); const aliasPaths = parsedTsConfig.compilerOptions?.paths ?? {}; for (const [alias, paths] of Object.entries(aliasPaths)) { if (paths.includes("./*") || paths.includes("./src/*") || paths.includes("./app/*") || paths.includes("./resources/js/*")) { const cleanAlias = alias.replace(/\/\*$/, "") ?? null; return cleanAlias === "#build" ? "@" : cleanAlias; } } return Object.keys(aliasPaths)?.[0]?.replace(/\/\*$/, "") ?? null; } async function isTypeScriptProject(cwd) { const files = await glob("tsconfig.*", { cwd, deep: 1, ignore: PROJECT_SHARED_IGNORE }); return files.length > 0; } async function getProjectConfig(cwd, defaultProjectInfo = null) { const [existingConfig, projectInfo] = await Promise.all([ getConfig(cwd), !defaultProjectInfo ? getProjectInfo(cwd) : Promise.resolve(defaultProjectInfo) ]); if (existingConfig) { return existingConfig; } if (!projectInfo || !projectInfo.tailwindCssFile || projectInfo.tailwindVersion === "v3" && !projectInfo.tailwindConfigFile) { return null; } const config = { $schema: "https://shadcn-vue.com/schema.json", typescript: projectInfo.typescript, style: "new-york", tailwind: { config: projectInfo.tailwindConfigFile ?? "", baseColor: "zinc", css: projectInfo.tailwindCssFile, cssVariables: true, prefix: "" }, iconLibrary: "lucide", aliases: { components: `${projectInfo.aliasPrefix}/components`, ui: `${projectInfo.aliasPrefix}/components/ui`, composables: `${projectInfo.aliasPrefix}/composables`, lib: `${projectInfo.aliasPrefix}/lib`, utils: `${projectInfo.aliasPrefix}/lib/utils` } }; return await resolveConfigPaths(cwd, config); } async function getProjectTailwindVersionFromConfig(config) { if (!config.resolvedPaths?.cwd) { return "v3"; } const projectInfo = await getProjectInfo(config.resolvedPaths.cwd); if (!projectInfo?.tailwindVersion) { return null; } return projectInfo.tailwindVersion; } // src/utils/resolve-import.ts import { createPathsMatcher } from "get-tsconfig"; function resolveImport(importPath, config) { const matcher = createPathsMatcher(config); if (matcher === null) { return; } const paths = matcher(importPath); return paths[0]; } // src/utils/get-config.ts import { cosmiconfig } from "cosmiconfig"; import { getTsconfig } from "get-tsconfig"; import path3 from "pathe"; import { z as z3 } from "zod"; // src/utils/highlighter.ts import { colors } from "consola/utils"; var highlighter = { error: colors.red, warn: colors.yellow, info: colors.cyan, success: colors.green }; // src/utils/get-config.ts var TAILWIND_CSS_PATH = { nuxt: "assets/css/tailwind.css", vite: "src/assets/index.css", laravel: "resources/css/app.css", astro: "src/styles/globals.css" }; var DEFAULT_COMPONENTS = "@/components"; var DEFAULT_UTILS = "@/lib/utils"; var DEFAULT_TAILWIND_CSS = TAILWIND_CSS_PATH.nuxt; var DEFAULT_TAILWIND_CONFIG = "tailwind.config.js"; var explorer = cosmiconfig("components", { searchPlaces: ["components.json"] }); var rawConfigSchema = z3.object({ $schema: z3.string().optional(), style: z3.string(), typescript: z3.boolean().default(true), tailwind: z3.object({ config: z3.string().optional(), css: z3.string(), baseColor: z3.string(), cssVariables: z3.boolean().default(true), prefix: z3.string().default("").optional() }), aliases: z3.object({ components: z3.string(), composables: z3.string().optional(), utils: z3.string(), ui: z3.string().optional(), lib: z3.string().optional() }), iconLibrary: z3.string().optional() }).strict(); var configSchema = rawConfigSchema.extend({ resolvedPaths: z3.object({ cwd: z3.string(), tailwindConfig: z3.string(), tailwindCss: z3.string(), utils: z3.string(), components: z3.string(), composables: z3.string(), lib: z3.string(), ui: z3.string() }) }); async function getConfig(cwd) { const config = await getRawConfig(cwd); if (!config) { return null; } if (!config.iconLibrary) { config.iconLibrary = config.style === "new-york" ? "radix" : "lucide"; } return await resolveConfigPaths(cwd, config); } function getTSConfig(cwd, tsconfigName) { const parsedConfig = getTsconfig(path3.resolve(cwd, "package.json"), tsconfigName); if (parsedConfig === null) { throw new Error( `Failed to find ${highlighter.info(tsconfigName)}` ); } return parsedConfig; } async function resolveConfigPaths(cwd, config) { const tsconfigType = config.typescript ? "tsconfig.json" : "jsconfig.json"; const tsConfig = getTSConfig(cwd, tsconfigType); return configSchema.parse({ ...config, resolvedPaths: { cwd, tailwindConfig: config.tailwind.config ? path3.resolve(cwd, config.tailwind.config) : "", tailwindCss: path3.resolve(cwd, config.tailwind.css), utils: await resolveImport(config.aliases.utils, tsConfig), components: await resolveImport(config.aliases.components, tsConfig), ui: config.aliases.ui ? await resolveImport(config.aliases.ui, tsConfig) : path3.resolve( await resolveImport(config.aliases.components, tsConfig) ?? cwd, "ui" ), // TODO: Make this configurable. // For now, we assume the lib and hooks directories are one level up from the components directory. lib: config.aliases.lib ? await resolveImport(config.aliases.lib, tsConfig) : path3.resolve( await resolveImport(config.aliases.utils, tsConfig) ?? cwd, ".." ), composables: config.aliases.composables ? await resolveImport(config.aliases.composables, tsConfig) : path3.resolve( await resolveImport(config.aliases.components, tsConfig) ?? cwd, "..", "composables" ) } }); } async function getRawConfig(cwd) { try { const configResult = await explorer.search(cwd); if (!configResult) { return null; } return rawConfigSchema.parse(configResult.config); } catch (error) { throw new Error(`Invalid configuration found in ${cwd}/components.json.`); } } async function getTargetStyleFromConfig(cwd, fallback) { const projectInfo = await getProjectInfo(cwd); return projectInfo?.tailwindVersion === "v4" ? "new-york-v4" : fallback; } // src/utils/handle-error.ts import { consola } from "consola"; function handleError(error) { consola.log("this is error: ", error); if (typeof error === "string") { consola.error(error); process.exit(1); } if (error instanceof Error) { consola.error(error.message); process.exit(1); } consola.error("Something went wrong. Please try again."); process.exit(1); } // src/utils/logger.ts import consola2 from "consola"; var logger = { error(...args) { consola2.log(highlighter.error(args.join(" "))); }, warn(...args) { consola2.log(highlighter.warn(args.join(" "))); }, info(...args) { consola2.log(highlighter.info(args.join(" "))); }, success(...args) { consola2.log(highlighter.success(args.join(" "))); }, log(...args) { consola2.log(args.join(" ")); }, break() { consola2.log(""); } }; // src/utils/updaters/update-tailwind-config.ts import { promises as fs3 } from "node:fs"; import { tmpdir } from "node:os"; // src/utils/spinner.ts import ora from "ora"; function spinner(text, options) { return ora({ text, isSilent: options?.silent }); } // src/utils/updaters/update-tailwind-config.ts import deepmerge from "deepmerge"; import path4 from "pathe"; import objectToString from "stringify-object"; import { Project, QuoteKind, ScriptKind, SyntaxKind } from "ts-morph"; async function updateTailwindConfig(tailwindConfig, config, options) { if (!tailwindConfig) { return; } options = { silent: false, tailwindVersion: "v3", ...options }; if (options.tailwindVersion === "v4") { return; } const tailwindFileRelativePath = path4.relative( config.resolvedPaths.cwd, config.resolvedPaths.tailwindConfig ); const tailwindSpinner = spinner( `Updating ${highlighter.info(tailwindFileRelativePath)}`, { silent: options.silent } ).start(); const raw = await fs3.readFile(config.resolvedPaths.tailwindConfig, "utf8"); const output = await transformTailwindConfig(raw, tailwindConfig, config); await fs3.writeFile(config.resolvedPaths.tailwindConfig, output, "utf8"); tailwindSpinner?.succeed(); } async function transformTailwindConfig(input, tailwindConfig, config) { const sourceFile = await _createSourceFile(input, config); const configObject = sourceFile.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression).find( (node) => node.getProperties().some( (property) => property.isKind(SyntaxKind.PropertyAssignment) && property.getName() === "content" ) ); if (!configObject) { return input; } const quoteChar = _getQuoteChar(configObject); addTailwindConfigProperty( configObject, { name: "darkMode", value: "class" }, { quoteChar } ); tailwindConfig.plugins?.forEach((plugin) => { addTailwindConfigPlugin(configObject, plugin); }); if (tailwindConfig.theme) { await addTailwindConfigTheme(configObject, tailwindConfig.theme); } return sourceFile.getFullText(); } function addTailwindConfigProperty(configObject, property, { quoteChar }) { const existingProperty = configObject.getProperty("darkMode"); if (!existingProperty) { const newProperty = { name: property.name, initializer: `[${quoteChar}${property.value}${quoteChar}]` }; if (property.name === "darkMode") { configObject.insertPropertyAssignment(0, newProperty); return configObject; } configObject.addPropertyAssignment(newProperty); return configObject; } if (existingProperty.isKind(SyntaxKind.PropertyAssignment)) { const initializer = existingProperty.getInitializer(); const newValue = `${quoteChar}${property.value}${quoteChar}`; if (initializer?.isKind(SyntaxKind.StringLiteral)) { const initializerText = initializer.getText(); initializer.replaceWithText(`[${initializerText}, ${newValue}]`); return configObject; } if (initializer?.isKind(SyntaxKind.ArrayLiteralExpression)) { if (initializer.getElements().map((element) => element.getText()).includes(newValue)) { return configObject; } initializer.addElement(newValue); } return configObject; } return configObject; } async function addTailwindConfigTheme(configObject, theme) { if (!configObject.getProperty("theme")) { configObject.addPropertyAssignment({ name: "theme", initializer: "{}" }); } nestSpreadProperties(configObject); const themeProperty = configObject.getPropertyOrThrow("theme")?.asKindOrThrow(SyntaxKind.PropertyAssignment); const themeInitializer = themeProperty.getInitializer(); if (themeInitializer?.isKind(SyntaxKind.ObjectLiteralExpression)) { const themeObjectString = themeInitializer.getText(); const themeObject = await parseObjectLiteral(themeObjectString); const result = deepmerge(themeObject, theme, { arrayMerge: (dst, src) => src }); const resultString = objectToString(result).replace(/'\.\.\.(.*)'/g, "...$1").replace(/'"/g, "'").replace(/"'/g, "'").replace(/'\[/g, "[").replace(/\]'/g, "]").replace(/'\\'/g, "'").replace(/\\'/g, "'").replace(/\\''/g, "'").replace(/''/g, "'"); themeInitializer.replaceWithText(resultString); } unnestSpreadProperties(configObject); } function addTailwindConfigPlugin(configObject, plugin) { const existingPlugins = configObject.getProperty("plugins"); if (!existingPlugins) { configObject.addPropertyAssignment({ name: "plugins", initializer: `[${plugin}]` }); return configObject; } if (existingPlugins.isKind(SyntaxKind.PropertyAssignment)) { const initializer = existingPlugins.getInitializer(); if (initializer?.isKind(SyntaxKind.ArrayLiteralExpression)) { if (initializer.getElements().map((element) => { return element.getText().replace(/["']/g, ""); }).includes(plugin.replace(/["']/g, ""))) { return configObject; } initializer.addElement(plugin); } return configObject; } return configObject; } async function _createSourceFile(input, config) { const dir = await fs3.mkdtemp(path4.join(tmpdir(), "shadcn-")); const resolvedPath = config?.resolvedPaths?.tailwindConfig || "tailwind.config.ts"; const tempFile = path4.join(dir, `shadcn-${path4.basename(resolvedPath)}`); const project = new Project({ compilerOptions: {} }); const sourceFile = project.createSourceFile(tempFile, input, { // Note: .js and .mjs can still be valid for TS projects. // We can't infer TypeScript from config.tsx. scriptKind: path4.extname(resolvedPath) === ".ts" ? ScriptKind.TS : ScriptKind.JS }); return sourceFile; } function _getQuoteChar(configObject) { return configObject.getFirstDescendantByKind(SyntaxKind.StringLiteral)?.getQuoteKind() === QuoteKind.Single ? "'" : '"'; } function nestSpreadProperties(obj) { const properties = obj.getProperties(); for (let i = 0; i < properties.length; i++) { const prop = properties[i]; if (prop.isKind(SyntaxKind.SpreadAssignment)) { const spreadAssignment = prop.asKindOrThrow(SyntaxKind.SpreadAssignment); const spreadText = spreadAssignment.getExpression().getText(); obj.insertPropertyAssignment(i, { // Need to escape the name with " so that deepmerge doesn't mishandle the key name: `"___${spreadText.replace(/^\.\.\./, "")}"`, initializer: `"...${spreadText.replace(/^\.\.\./, "")}"` }); spreadAssignment.remove(); } else if (prop.isKind(SyntaxKind.PropertyAssignment)) { const propAssignment = prop.asKindOrThrow(SyntaxKind.PropertyAssignment); const initializer = propAssignment.getInitializer(); if (initializer && initializer.isKind(SyntaxKind.ObjectLiteralExpression)) { nestSpreadProperties( initializer.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) ); } else if (initializer && initializer.isKind(SyntaxKind.ArrayLiteralExpression)) { nestSpreadElements( initializer.asKindOrThrow(SyntaxKind.ArrayLiteralExpression) ); } } } } function nestSpreadElements(arr) { const elements = arr.getElements(); for (let j = 0; j < elements.length; j++) { const element = elements[j]; if (element.isKind(SyntaxKind.ObjectLiteralExpression)) { nestSpreadProperties( element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) ); } else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) { nestSpreadElements( element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression) ); } else if (element.isKind(SyntaxKind.SpreadElement)) { const spreadText = element.getText(); arr.removeElement(j); arr.insertElement(j, `"${spreadText}"`); } } } function unnestSpreadProperties(obj) { const properties = obj.getProperties(); for (let i = 0; i < properties.length; i++) { const prop = properties[i]; if (prop.isKind(SyntaxKind.PropertyAssignment)) { const propAssignment = prop; const initializer = propAssignment.getInitializer(); if (initializer && initializer.isKind(SyntaxKind.StringLiteral)) { const value = initializer.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue(); if (value.startsWith("...")) { obj.insertSpreadAssignment(i, { expression: value.slice(3) }); propAssignment.remove(); } } else if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) { unnestSpreadProperties(initializer); } else if (initializer && initializer.isKind(SyntaxKind.ArrayLiteralExpression)) { unnsetSpreadElements( initializer.asKindOrThrow(SyntaxKind.ArrayLiteralExpression) ); } } } } function unnsetSpreadElements(arr) { const elements = arr.getElements(); for (let j = 0; j < elements.length; j++) { const element = elements[j]; if (element.isKind(SyntaxKind.ObjectLiteralExpression)) { unnestSpreadProperties( element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) ); } else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) { unnsetSpreadElements( element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression) ); } else if (element.isKind(SyntaxKind.StringLiteral)) { const spreadText = element.getText(); const spreadTest = /^['"](\.\.\..*)['"]$/g; if (spreadTest.test(spreadText)) { arr.removeElement(j); arr.insertElement(j, spreadText.replace(spreadTest, "$1")); } } } } async function parseObjectLiteral(objectLiteralString) { const sourceFile = await _createSourceFile( `const theme = ${objectLiteralString}`, null ); const statement = sourceFile.getStatements()[0]; if (statement?.getKind() === SyntaxKind.VariableStatement) { const declaration = statement.getDeclarationList()?.getDeclarations()[0]; const initializer = declaration.getInitializer(); if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) { return await parseObjectLiteralExpression(initializer); } } throw new Error("Invalid input: not an object literal"); } function parseObjectLiteralExpression(node) { const result = {}; for (const property of node.getProperties()) { if (property.isKind(SyntaxKind.PropertyAssignment)) { const name = property.getName().replace(/'/g, ""); if (property.getInitializer()?.isKind(SyntaxKind.ObjectLiteralExpression)) { result[name] = parseObjectLiteralExpression( property.getInitializer() ); } else if (property.getInitializer()?.isKind(SyntaxKind.ArrayLiteralExpression)) { result[name] = parseArrayLiteralExpression( property.getInitializer() ); } else { result[name] = parseValue(property.getInitializer()); } } } return result; } function parseArrayLiteralExpression(node) { const result = []; for (const element of node.getElements()) { if (element.isKind(SyntaxKind.ObjectLiteralExpression)) { result.push( parseObjectLiteralExpression( element.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) ) ); } else if (element.isKind(SyntaxKind.ArrayLiteralExpression)) { result.push( parseArrayLiteralExpression( element.asKindOrThrow(SyntaxKind.ArrayLiteralExpression) ) ); } else { result.push(parseValue(element)); } } return result; } function parseValue(node) { switch (node.getKind()) { case SyntaxKind.StringLiteral: return node.getText(); case SyntaxKind.NumericLiteral: return Number(node.getText()); case SyntaxKind.TrueKeyword: return true; case SyntaxKind.FalseKeyword: return false; case SyntaxKind.NullKeyword: return null; case SyntaxKind.ArrayLiteralExpression: return node.getElements().map(parseValue); case SyntaxKind.ObjectLiteralExpression: return parseObjectLiteralExpression(node); default: return node.getText(); } } function buildTailwindThemeColorsFromCssVars(cssVars) { const result = {}; for (const key of Object.keys(cssVars)) { const parts = key.split("-"); const colorName = parts[0]; const subType = parts.slice(1).join("-"); if (subType === "") { if (typeof result[colorName] === "object") { result[colorName].DEFAULT = `hsl(var(--${key}))`; } else { result[colorName] = `hsl(var(--${key}))`; } } else { if (typeof result[colorName] !== "object") { result[colorName] = { DEFAULT: `hsl(var(--${colorName}))` }; } result[colorName][subType] = `hsl(var(--${key}))`; } } for (const [colorName, value] of Object.entries(result)) { if (typeof value === "object" && value.DEFAULT === `hsl(var(--${colorName}))` && !(colorName in cssVars)) { delete value.DEFAULT; } } return result; } // src/registry/api.ts import deepmerge2 from "deepmerge"; import { ofetch } from "ofetch"; import path5 from "pathe"; import { ProxyAgent } from "undici"; import { z as z4 } from "zod"; var REGISTRY_URL = process.env.REGISTRY_URL ?? "https://shadcn-vue.com/r"; var agent = process.env.https_proxy ? new ProxyAgent(process.env.https_proxy) : void 0; var registryCache = /* @__PURE__ */ new Map(); async function getRegistryIndex() { try { const [result] = await fetchRegistry(["index.json"]); return registryIndexSchema.parse(result); } catch (error) { logger.error("\n"); handleError(error); } } async function getRegistryStyles() { try { const [result] = await fetchRegistry(["styles/index.json"]); return stylesSchema.parse(result); } catch (error) { logger.error("\n"); handleError(error); return []; } } async function getRegistryIcons() { try { const [result] = await fetchRegistry(["icons/index.json"]); return iconsSchema.parse(result); } catch (error) { handleError(error); return {}; } } async function getRegistryItem(name, style) { try { const [result] = await fetchRegistry([ isUrl(name) ? name : `styles/${style}/${name}.json` ]); return registryItemSchema.parse(result); } catch (error) { logger.break(); handleError(error); return null; } } var BASE_COLORS = [ { name: "neutral", label: "Neutral" }, { name: "gray", label: "Gray" }, { name: "zinc", label: "Zinc" }, { name: "stone", label: "Stone" }, { name: "slate", label: "Slate" } ]; async function getRegistryBaseColors() { return BASE_COLORS; } async function getRegistryBaseColor(baseColor) { try { const [result] = await fetchRegistry([`colors/${baseColor}.json`]); return registryBaseColorSchema.parse(result); } catch (error) { handleError(error); } } async function fetchTree(style, tree) { try { const paths = tree.map((item) => `styles/${style}/${item.name}.json`); const result = await fetchRegistry(paths); return registryIndexSchema.parse(result); } catch (error) { handleError(error); } } async function getItemTargetPath(config, item, override) { if (override) { return override; } if (item.type === "registry:ui") { return config.resolvedPaths.ui ?? config.resolvedPaths.components; } const [parent, type] = item.type?.split(":") ?? []; if (!(parent in config.resolvedPaths)) { return null; } return path5.join( config.resolvedPaths[parent], type ); } async function fetchRegistry(paths) { try { const results = await Promise.all( paths.map(async (path6) => { const url = getRegistryUrl(path6); if (registryCache.has(url)) { return registryCache.get(url); } const response = await ofetch(url, { dispatcher: agent, parseResponse: JSON.parse }).catch((error) => { throw new Error(error.data); }); registryCache.set(url, response); return response; }) ); return results; } catch (error) { logger.error("\n"); handleError(error); return []; } } async function registryResolveItemsTree(names, config) { try { const index = await getRegistryIndex(); if (!index) { return null; } if (names.includes("index")) { names.unshift("index"); } const registryDependencies = /* @__PURE__ */ new Set(); for (const name of names) { const itemRegistryDependencies = await resolveRegistryDependencies( name, config ); itemRegistryDependencies.forEach((dep) => registryDependencies.add(dep)); } const uniqueRegistryDependencies = Array.from(registryDependencies); const result = await fetchRegistry(uniqueRegistryDependencies); const payload = z4.array(registryItemSchema).parse(result); if (!payload) { return null; } if (names.includes("index")) { if (config.tailwind.baseColor) { const theme = await registryGetTheme(config.tailwind.baseColor, config); if (theme) { payload.unshift(theme); } } } let tailwind = {}; payload.forEach((item) => { tailwind = deepmerge2(tailwind, item.tailwind ?? {}); }); let cssVars = {}; payload.forEach((item) => { cssVars = deepmerge2(cssVars, item.cssVars ?? {}); }); let docs = ""; payload.forEach((item) => { if (item.docs) { docs += `${item.docs} `; } }); return registryResolvedItemsTreeSchema.parse({ dependencies: Array.from(new Set(payload.flatMap((item) => item.dependencies ?? []))), devDependencies: Array.from(new Set(payload.flatMap((item) => item.devDependencies ?? []))), files: deepmerge2.all(payload.map((item) => item.files ?? [])), tailwind, cssVars, docs }); } catch (error) { handleError(error); return null; } } async function resolveRegistryDependencies(url, config) { const visited = /* @__PURE__ */ new Set(); const payload = []; const style = config.resolvedPaths?.cwd ? await getTargetStyleFromConfig(config.resolvedPaths.cwd, config.style) : config.style; async function resolveDependencies(itemUrl) { const url2 = getRegistryUrl( isUrl(itemUrl) ? itemUrl : `styles/${style}/${itemUrl}.json` ); if (visited.has(url2)) { return; } visited.add(url2); try { const [result] = await fetchRegistry([url2]); const item = registryItemSchema.parse(result); payload.push(url2); 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)); } async function registryGetTheme(name, config) { const [baseColor, tailwindVersion] = await Promise.all([ getRegistryBaseColor(name), getProjectTailwindVersionFromConfig(config) ]); if (!baseColor) { return null; } const theme = { name, type: "registry:theme", tailwind: { config: { theme: { extend: { borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)" }, colors: {} } } } }, cssVars: { theme: {}, light: { radius: "0.5rem" }, dark: {} } }; if (config.tailwind.cssVariables) { theme.tailwind.config.theme.extend.colors = { ...theme.tailwind.config.theme.extend.colors, ...buildTailwindThemeColorsFromCssVars(baseColor.cssVars.dark ?? {}) }; theme.cssVars = { theme: { ...baseColor.cssVars.theme, ...theme.cssVars.theme }, light: { ...baseColor.cssVars.light, ...theme.cssVars.light }, dark: { ...baseColor.cssVars.dark, ...theme.cssVars.dark } }; if (tailwindVersion === "v4" && baseColor.cssVarsV4) { theme.cssVars = { theme: { ...baseColor.cssVarsV4.theme, ...theme.cssVars.theme }, light: { ...theme.cssVars.light, ...baseColor.cssVarsV4.light }, dark: { ...theme.cssVars.dark, ...baseColor.cssVarsV4.dark } }; } } return theme; } function getRegistryUrl(path6) { if (isUrl(path6)) { const url = new URL(path6); if (url.pathname.match(/\/chat\/b\//) && !url.pathname.endsWith("/json")) { url.pathname = `${url.pathname}/json`; } return url.toString(); } return `${REGISTRY_URL}/${path6}`; } function isUrl(path6) { try { new URL(path6); return true; } catch (error) { return false; } } async function resolveRegistryItems(names, config) { const registryDependencies = []; for (const name of names) { const itemRegistryDependencies = await resolveRegistryDependencies( name, config ); registryDependencies.push(...itemRegistryDependencies); } return Array.from(new Set(registryDependencies)); } export { highlighter, DEFAULT_COMPONENTS, DEFAULT_UTILS, DEFAULT_TAILWIND_CSS, DEFAULT_TAILWIND_CONFIG, rawConfigSchema, getConfig, resolveConfigPaths, getPackageInfo, getProjectInfo, getProjectConfig, getProjectTailwindVersionFromConfig, logger, spinner, registryItemTypeSchema, registryItemFileSchema, registryItemTailwindSchema, registryItemCssVarsSchema, registryItemCssSchema, registryItemSchema, registrySchema, registryIndexSchema, stylesSchema, iconsSchema, registryBaseColorSchema, registryResolvedItemsTreeSchema, handleError, updateTailwindConfig, _createSourceFile, _getQuoteChar, getRegistryIndex, getRegistryStyles, getRegistryIcons, getRegistryItem, BASE_COLORS, getRegistryBaseColors, getRegistryBaseColor, fetchTree, getItemTargetPath, fetchRegistry, registryResolveItemsTree, isUrl, resolveRegistryItems }; //# sourceMappingURL=chunk-MOIE35VS.js.map