From 093eb419a846e11b27ea67c248ccedb1e3adfedd Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 16 Feb 2026 15:23:55 +0400 Subject: [PATCH] fix --- .changeset/few-ghosts-move.md | 1 + packages/shadcn/src/commands/init.ts | 14 +++++- packages/shadcn/src/utils/presets.ts | 17 +++++-- packages/tests/src/tests/add.test.ts | 48 +++++++++---------- .../tests/src/tests/create-monorepo.test.ts | 8 ++-- 5 files changed, 56 insertions(+), 32 deletions(-) diff --git a/.changeset/few-ghosts-move.md b/.changeset/few-ghosts-move.md index ea7e19ff40..3067f462bd 100644 --- a/.changeset/few-ghosts-move.md +++ b/.changeset/few-ghosts-move.md @@ -6,6 +6,7 @@ added `--preset` flag to `init` command ## Breaking changes +- The `--base-color` flag has been removed. Use `--preset` with a custom preset URL instead. - The `--src-dir` and `--no-src-dir` flags have been removed. Use create-next-app then run shadcn init. - The `--no-base-style` flag has been removed. Use `extends: none` in your registry items. - The `--css-variables` and `--no-css-variables` flags have been removed from the `add` command. diff --git a/packages/shadcn/src/commands/init.ts b/packages/shadcn/src/commands/init.ts index b2fc9d1a18..c61620dafa 100644 --- a/packages/shadcn/src/commands/init.ts +++ b/packages/shadcn/src/commands/init.ts @@ -251,7 +251,17 @@ export const init = new Command() parsedOptions.cwd, "components.json" ) + + // Read existing registries before backing up. + let existingRegistries if (fsExtra.existsSync(componentsJsonPath)) { + try { + const existingConfig = await fsExtra.readJson(componentsJsonPath) + existingRegistries = existingConfig.registries + } catch { + // Ignore read errors. + } + componentsJsonBackupPath = createFileBackup(componentsJsonPath) ?? undefined if (!componentsJsonBackupPath) { @@ -263,7 +273,9 @@ export const init = new Command() // Resolve registry:base config from the first component. const { registryBaseConfig, installStyleIndex } = - await resolveRegistryBaseConfig(components[0], parsedOptions.cwd) + await resolveRegistryBaseConfig(components[0], parsedOptions.cwd, { + registries: existingRegistries, + }) if (!installStyleIndex) { parsedOptions.installStyleIndex = false diff --git a/packages/shadcn/src/utils/presets.ts b/packages/shadcn/src/utils/presets.ts index 718be9593f..c02bb66192 100644 --- a/packages/shadcn/src/utils/presets.ts +++ b/packages/shadcn/src/utils/presets.ts @@ -2,6 +2,7 @@ import { getRegistryItems } from "@/src/registry/api" import { buildUrlAndHeadersForRegistryItem } from "@/src/registry/builder" import { configWithDefaults } from "@/src/registry/config" import { REGISTRY_URL } from "@/src/registry/constants" +import { type registryConfigSchema } from "@/src/registry/schema" import { type Preset } from "@/src/schema" import { createConfig } from "@/src/utils/get-config" import { highlighter } from "@/src/utils/highlighter" @@ -9,6 +10,7 @@ import { logger } from "@/src/utils/logger" import { ensureRegistriesInConfig } from "@/src/utils/registries" import open from "open" import prompts from "prompts" +import { type z } from "zod" const SHADCN_URL = REGISTRY_URL.replace(/\/r\/?$/, "") @@ -154,13 +156,20 @@ export async function promptForPreset(options: { return resolveInitUrl({ ...preset, rtl: options.rtl }) } -export async function resolveRegistryBaseConfig(initUrl: string, cwd: string) { +export async function resolveRegistryBaseConfig( + initUrl: string, + cwd: string, + options?: { + registries?: z.infer + } +) { // Use a shadow config to fetch the registry:base item. let shadowConfig = configWithDefaults( createConfig({ resolvedPaths: { cwd, }, + ...(options?.registries && { registries: options.registries }), }) ) @@ -182,9 +191,11 @@ export async function resolveRegistryBaseConfig(initUrl: string, cwd: string) { config: shadowConfig, }) + const registryBaseConfig = + item?.type === "registry:base" && item.config ? item.config : undefined + return { - registryBaseConfig: - item?.type === "registry:base" && item.config ? item.config : undefined, + registryBaseConfig, installStyleIndex: item?.extends !== "none", } } diff --git a/packages/tests/src/tests/add.test.ts b/packages/tests/src/tests/add.test.ts index 4c865557cb..e146a0332d 100644 --- a/packages/tests/src/tests/add.test.ts +++ b/packages/tests/src/tests/add.test.ts @@ -15,7 +15,7 @@ import { describe("shadcn add", () => { it("should add item to project", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "button"]) expect( await fs.pathExists(path.join(fixturePath, "components/ui/button.tsx")) @@ -24,7 +24,7 @@ describe("shadcn add", () => { it("should add multiple items to project", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "button", "card"]) expect( await fs.pathExists(path.join(fixturePath, "components/ui/button.tsx")) @@ -36,7 +36,7 @@ describe("shadcn add", () => { it("should add item from url", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) const registryUrl = getRegistryUrl() const url = `${registryUrl}/styles/new-york-v4/login-01.json` await npxShadcn(fixturePath, ["add", url]) @@ -60,7 +60,7 @@ describe("shadcn add", () => { it("should add component from local file", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-component.json", @@ -75,7 +75,7 @@ describe("shadcn add", () => { it("should add registry:page to the correct path", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "login-03"]) expect( await fs.pathExists(path.join(fixturePath, "app/login/page.tsx")) @@ -84,7 +84,7 @@ describe("shadcn add", () => { it("should add item with registryDependencies", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "alert-dialog"]) expect( await fs.pathExists( @@ -98,7 +98,7 @@ describe("shadcn add", () => { it("should add item with npm dependencies", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-style.json", @@ -112,7 +112,7 @@ describe("shadcn add", () => { it("should install cssVars", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-style.json", @@ -153,7 +153,7 @@ describe("shadcn add", () => { it("should add item with target", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-item.json", @@ -168,7 +168,7 @@ describe("shadcn add", () => { it("should add item with target to src", async () => { const fixturePath = await createFixtureTestDirectory("vite-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-item.json", @@ -183,7 +183,7 @@ describe("shadcn add", () => { it("should add item with target to root", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-item-to-root.json", @@ -198,7 +198,7 @@ describe("shadcn add", () => { it("should add item with target to root when src", async () => { const fixturePath = await createFixtureTestDirectory("vite-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-item-to-root.json", @@ -213,7 +213,7 @@ describe("shadcn add", () => { it("should add item with envVars", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "../../fixtures/registry/example-env-vars.json", @@ -232,7 +232,7 @@ describe("shadcn add", () => { it("should add NOT update existing envVars", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await fs.writeFile( path.join(fixturePath, ".env.local"), @@ -257,7 +257,7 @@ describe("shadcn add", () => { it("should use existing .env if it exists", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await fs.writeFile( path.join(fixturePath, ".env"), @@ -299,7 +299,7 @@ describe("shadcn add", () => { it("should add component to custom file path", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "add", "button", @@ -316,7 +316,7 @@ describe("shadcn add", () => { it("should add component to custom directory", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "button", "--path=custom/components"]) expect( @@ -331,7 +331,7 @@ describe("shadcn add", () => { it("should add multiple files to custom directory", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["add", "button", "card", "--path=custom/ui"]) expect( @@ -379,7 +379,7 @@ describe("shadcn add", () => { describe("shadcn registry add", () => { it("should add registry from index to components.json", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["registry", "add", "@magicui"]) const componentsJson = await fs.readJson( @@ -392,7 +392,7 @@ describe("shadcn registry add", () => { it("should add custom registry with URL", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, [ "registry", "add", @@ -409,7 +409,7 @@ describe("shadcn registry add", () => { it("should add multiple registries", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["registry", "add", "@magicui", "@aceternity"]) const componentsJson = await fs.readJson( @@ -421,7 +421,7 @@ describe("shadcn registry add", () => { it("should skip already configured registries", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) await npxShadcn(fixturePath, ["registry", "add", "@magicui"]) // Add again - should not error. @@ -435,7 +435,7 @@ describe("shadcn registry add", () => { it("should error for registry not in index without URL", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) const result = await npxShadcn(fixturePath, [ "registry", @@ -448,7 +448,7 @@ describe("shadcn registry add", () => { it("should error for invalid URL missing {name}", async () => { const fixturePath = await createFixtureTestDirectory("next-app") - await npxShadcn(fixturePath, ["init", "--defaults", "--base-color=neutral"]) + await npxShadcn(fixturePath, ["init", "--defaults"]) const result = await npxShadcn(fixturePath, [ "registry", diff --git a/packages/tests/src/tests/create-monorepo.test.ts b/packages/tests/src/tests/create-monorepo.test.ts index 662899ca0b..13f3d0b697 100644 --- a/packages/tests/src/tests/create-monorepo.test.ts +++ b/packages/tests/src/tests/create-monorepo.test.ts @@ -40,7 +40,7 @@ describe("shadcn create - next-monorepo", () => { path.join(projectPath, "packages/ui/components.json") ) expect(uiConfig.style).toBe("radix-nova") - expect(uiConfig.iconLibrary).toBe("hugeicons") + expect(uiConfig.iconLibrary).toBe("lucide") expect(uiConfig.tailwind.css).toBe("src/styles/globals.css") expect(uiConfig.tailwind.baseColor).toBe("neutral") expect(uiConfig.tailwind.cssVariables).toBe(true) @@ -52,7 +52,7 @@ describe("shadcn create - next-monorepo", () => { path.join(projectPath, "apps/web/components.json") ) expect(webConfig.style).toBe("radix-nova") - expect(webConfig.iconLibrary).toBe("hugeicons") + expect(webConfig.iconLibrary).toBe("lucide") expect(webConfig.tailwind.css).toBe( "../../packages/ui/src/styles/globals.css" ) @@ -168,13 +168,13 @@ describe("shadcn create - next-monorepo", () => { ) expect(uiConfig.style).toBe("radix-nova") expect(uiConfig.iconLibrary).toBe("lucide") - expect(uiConfig.tailwind.baseColor).toBe("zinc") + expect(uiConfig.tailwind.baseColor).toBe("neutral") const webConfig = await fs.readJson( path.join(projectPath, "apps/web/components.json") ) expect(webConfig.style).toBe("radix-nova") - expect(webConfig.tailwind.baseColor).toBe("zinc") + expect(webConfig.tailwind.baseColor).toBe("neutral") // Verify CSS has zinc color theme applied. const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css")