diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts index 7cfb16c75..92b1eed36 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts @@ -119,6 +119,10 @@ describe('is-safe-migration', async () => { [`function Button({ variant='outline' }) {}`, 'outline'], [`Button({ variant: "outline" })`, 'outline'], [`Button({ variant: 'outline' })`, 'outline'], + [`Button({ variant: isActive ? "outline" : "ghost" })`, 'outline'], + [``, 'outline'], + [``, 'outline'], + [``, 'outline'], ])('does not replace classes in invalid positions #%#', async (example, candidate) => { expect( await migrateCandidate(designSystem, {}, candidate, { @@ -142,6 +146,15 @@ describe('is-safe-migration', async () => { // Preact-style [`
`, 'shadow', 'shadow-sm'], + + // A conditional class is still a class, even next to a `variant` prop + [``, 'shadow', 'shadow-sm'], + [``, 'shadow', 'shadow-sm'], + [ + `Button({ variant: a ? "ghost" : "x", className: b ? "shadow" : "y" })`, + 'shadow', + 'shadow-sm', + ], ])('replaces classes in valid positions #%#', async (example, candidate, expected) => { expect( await migrateCandidate(designSystem, {}, candidate, { diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts index 2c09f299f..278e2dd0b 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts @@ -17,8 +17,10 @@ const CONDITIONAL_TEMPLATE_SYNTAX = [ // Alpine /wire:[^\s]*?$/, - // shadcn/ui variants - /variant\s*[:=]\s*\{?['"`]$/, + // shadcn/ui variants, including a ternary or nullish branch between the prop + // and the literal. Braces and commas are excluded so the match cannot reach + // out of its own value into a neighbouring prop such as `className`. + /variant\s*[:=]\s*\{?(?:[^{},]*?(?:\?\?|\?|:)\s*)?['"`]$/, ] const NEXT_PLACEHOLDER_PROP = /placeholder=\{?['"`]$/ const VUE_3_EMIT = /\b\$?emit\(['"`]$/