From 783aef9ea26a5f069ffd662f0049dbcd87811dc6 Mon Sep 17 00:00:00 2001 From: dbpolito Date: Tue, 7 Apr 2026 21:47:18 -0300 Subject: [PATCH 1/2] fix: ignore inline style candidates during migration - treat inline style attributes as unsafe migration contexts - avoid misclassifying CSS property names like flex-grow and flex-shrink - add regression coverage for inline style attribute cases --- .../src/codemods/template/is-safe-migration.test.ts | 2 ++ .../src/codemods/template/is-safe-migration.ts | 7 +++++++ 2 files changed, 9 insertions(+) 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 ff12fc114c13..fafee6239afb 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 @@ -48,6 +48,8 @@ describe('is-safe-migration', async () => { [`
\n`, 'shadow'], [`
\n`, 'shadow'], [`
\n`, 'shadow'], + [`
\n`, 'flex-grow'], + [`
\n`, 'flex-shrink'], // Next.js Image placeholder cases [``, 'blur'], 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 fa7ee7f4a1c5..a0790a9dd3a8 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts @@ -20,6 +20,7 @@ const CONDITIONAL_TEMPLATE_SYNTAX = [ // shadcn/ui variants /variant\s*[:=]\s*\{?['"`]$/, ] +const INLINE_STYLE_ATTRIBUTE = /(?:^|\s):?style\s*=\s*['"]$/i const NEXT_PLACEHOLDER_PROP = /placeholder=\{?['"`]$/ const VUE_3_EMIT = /\b\$?emit\(['"`]$/ @@ -140,6 +141,12 @@ export function isSafeMigration( currentLineAfterCandidate += char } + // Inline `style="..."` attributes can contain CSS property names that look + // like valid utility candidates, such as `flex-grow`. + if (INLINE_STYLE_ATTRIBUTE.test(currentLineBeforeCandidate)) { + return false + } + // Heuristic: Require the candidate to be inside quotes let isQuoteBeforeCandidate = isMiddleOfString(currentLineBeforeCandidate) let isQuoteAfterCandidate = isMiddleOfString(currentLineAfterCandidate) From 964fc5466bd7a6b3c555ca97267df67df68b8175 Mon Sep 17 00:00:00 2001 From: dbpolito Date: Tue, 7 Apr 2026 21:54:40 -0300 Subject: [PATCH 2/2] fix: ignore inline style candidates during migration - skip inline style attribute values before candidate parsing - prevent CSS property names like flex-grow from being treated as utilities - keep template migration safety checks focused on actual class-like content --- .../codemods/template/is-safe-migration.ts | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) 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 a0790a9dd3a8..f77cbb710d22 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts @@ -67,6 +67,29 @@ export function isSafeMigration( } } + let currentLineBeforeCandidate = '' + for (let i = location.start - 1; i >= 0; i--) { + let char = location.contents.at(i)! + if (char === '\n') { + break + } + currentLineBeforeCandidate = char + currentLineBeforeCandidate + } + let currentLineAfterCandidate = '' + for (let i = location.end; i < location.contents.length; i++) { + let char = location.contents.at(i)! + if (char === '\n') { + break + } + currentLineAfterCandidate += char + } + + // Inline `style="..."` attributes can contain CSS property names that look + // like valid utility candidates, such as `flex-grow`. + if (INLINE_STYLE_ATTRIBUTE.test(currentLineBeforeCandidate)) { + return false + } + let [candidate] = parseCandidate(rawCandidate, designSystem) // If we can't parse the candidate, then it's not a candidate at all. However, @@ -124,29 +147,6 @@ export function isSafeMigration( } } - let currentLineBeforeCandidate = '' - for (let i = location.start - 1; i >= 0; i--) { - let char = location.contents.at(i)! - if (char === '\n') { - break - } - currentLineBeforeCandidate = char + currentLineBeforeCandidate - } - let currentLineAfterCandidate = '' - for (let i = location.end; i < location.contents.length; i++) { - let char = location.contents.at(i)! - if (char === '\n') { - break - } - currentLineAfterCandidate += char - } - - // Inline `style="..."` attributes can contain CSS property names that look - // like valid utility candidates, such as `flex-grow`. - if (INLINE_STYLE_ATTRIBUTE.test(currentLineBeforeCandidate)) { - return false - } - // Heuristic: Require the candidate to be inside quotes let isQuoteBeforeCandidate = isMiddleOfString(currentLineBeforeCandidate) let isQuoteAfterCandidate = isMiddleOfString(currentLineAfterCandidate)