diff --git a/packages/shadcn/src/utils/dry-run-formatter.ts b/packages/shadcn/src/utils/dry-run-formatter.ts index bd6b2bb3cb..d30cfbf33f 100644 --- a/packages/shadcn/src/utils/dry-run-formatter.ts +++ b/packages/shadcn/src/utils/dry-run-formatter.ts @@ -134,9 +134,7 @@ function formatSummaryOutput(result: DryRunResult, componentNames: string[]) { // Footer. lines.push(`${dim("│")} ${dim("Run with --diff to view changes.")}`) - lines.push( - `${dim("│")} ${dim("Run with --view to view file contents.")}` - ) + lines.push(`${dim("│")} ${dim("Run with --view to view file contents.")}`) lines.push(`${dim("└")} ${dim("Run without --dry-run to apply.")}`) return lines.join("\n") diff --git a/packages/shadcn/src/utils/updaters/update-css.ts b/packages/shadcn/src/utils/updaters/update-css.ts index eb9b46cdf2..1727ff9ceb 100644 --- a/packages/shadcn/src/utils/updaters/update-css.ts +++ b/packages/shadcn/src/utils/updaters/update-css.ts @@ -15,6 +15,7 @@ import AtRule from "postcss/lib/at-rule" import Declaration from "postcss/lib/declaration" import Root from "postcss/lib/root" import Rule from "postcss/lib/rule" +import { twMerge } from "tailwind-merge" import { z } from "zod" export async function updateCss( @@ -564,13 +565,10 @@ function processRule(parent: Root | AtRule, selector: string, properties: any) { node.type === "atrule" && node.name === "apply" ) if (existingApply) { - const existingClasses = new Set( - existingApply.params.split(/\s+/) + existingApply.params = twMerge( + existingApply.params, + atRuleParams ) - for (const cls of atRuleParams.split(/\s+/)) { - existingClasses.add(cls) - } - existingApply.params = [...existingClasses].join(" ") continue } } diff --git a/packages/shadcn/test/utils/updaters/update-css.test.ts b/packages/shadcn/test/utils/updaters/update-css.test.ts index e168f0dfeb..bd582569bc 100644 --- a/packages/shadcn/test/utils/updaters/update-css.test.ts +++ b/packages/shadcn/test/utils/updaters/update-css.test.ts @@ -1037,6 +1037,90 @@ describe("transformCss", () => { `) }) + test("should merge @apply directives in the same rule instead of duplicating", async () => { + const input = `@import "tailwindcss"; + +@layer base { + body { + @apply bg-background text-foreground; + } +}` + + const result = await transformCss(input, { + "@layer base": { + body: { + "@apply font-sans": {}, + }, + }, + }) + + expect(result).toMatchInlineSnapshot(` + "@import "tailwindcss"; + + @layer base { + body { + @apply bg-background text-foreground font-sans; + } + }" + `) + }) + + test("should resolve conflicting tailwind classes when merging @apply", async () => { + const input = `@import "tailwindcss"; + +@layer base { + body { + @apply bg-background font-serif text-foreground; + } +}` + + const result = await transformCss(input, { + "@layer base": { + body: { + "@apply font-mono": {}, + }, + }, + }) + + expect(result).toMatchInlineSnapshot(` + "@import "tailwindcss"; + + @layer base { + body { + @apply bg-background text-foreground font-mono; + } + }" + `) + }) + + test("should not duplicate @apply classes that already exist", async () => { + const input = `@import "tailwindcss"; + +@layer base { + body { + @apply bg-background text-foreground font-sans; + } +}` + + const result = await transformCss(input, { + "@layer base": { + body: { + "@apply font-sans": {}, + }, + }, + }) + + expect(result).toMatchInlineSnapshot(` + "@import "tailwindcss"; + + @layer base { + body { + @apply bg-background text-foreground font-sans; + } + }" + `) + }) + test("should replace existing keyframes instead of duplicating", async () => { const input = `@import "tailwindcss";