This commit is contained in:
shadcn
2026-02-16 15:23:55 +04:00
parent ce1f9259bf
commit 093eb419a8
5 changed files with 56 additions and 32 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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<typeof registryConfigSchema>
}
) {
// 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",
}
}

View File

@@ -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",

View File

@@ -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")