From c7d368b3c42de46e44b17c71d3bf9a9c6b3bc5cb Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 16 May 2025 12:53:34 +0200 Subject: [PATCH] Do not migrate declarations in `` blocks. We do this by making sure that: 1. We detect a declaration, the current heuristic is that the candidate is: - Preceded by whitespace - Followed by a colon and whitespace ```html ``` 2. We are in a `` block ```html ^^^^^^^^ ``` The reason we have these 2 checks is because just relying on the first heuristic alone, also means that we will not be migrating keys in JS objects, because they typically follow the same structure: ```js let classes = { flex: 0, ^ ^^ } ``` Another important thing to note is that we can't just ignore anything in between `` blocks, because you could still be using `@apply` that we _do_ want to migrate. Last but not least, the first heuristics is not perfect either. If you are writing minified CSS then this will likely fail if there is no whitespace around the candidate. But my current assumption is that nobody should be writing minified CSS, and minified CSS will very likely be generated and gitignored. In either situation, replacements in minified CSS will not be any worse than it is today. I'm open to suggestions for better heuristics. ## Test plan 1. Added an integration test that verifies that we do migrate `@apply` and don't migrate the `flex-shrink: 0;` declaration. Fixes: #17975 --- CHANGELOG.md | 4 +- integrations/upgrade/index.test.ts | 81 +++++++++++++++++++ .../codemods/template/is-safe-migration.ts | 31 +++++++ 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f48d1c51..a8a414fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Fixed + +- Upgrade: Do not migrate declarations that look like candidates in ` + `, + 'src/input.css': css` + @import 'tailwindcss'; + + .foo { + flex-shrink: 1; + } + + .bar { + @apply !underline; + } + `, + }, + }, + async ({ exec, fs, expect }) => { + await exec('npx @tailwindcss/upgrade') + + expect(await fs.dumpFiles('./src/**/*.{css,vue}')).toMatchInlineSnapshot(` + " + --- ./src/index.vue --- + + + + + --- ./src/input.css --- + @import 'tailwindcss'; + + .foo { + flex-shrink: 1; + } + + .bar { + @apply underline!; + } + " + `) + }, +) + function withBOM(text: string): string { return '\uFEFF' + text } 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 dd540d5c5..a344e56ed 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.ts @@ -22,6 +22,37 @@ export function isSafeMigration( location: { contents: string; start: number; end: number }, designSystem: DesignSystem, ): boolean { + // Ensure we are not migrating a candidate in a ` + // ``` + if ( + // Whitespace before the candidate + location.contents[location.start - 1]?.match(/\s/) && + // A colon followed by whitespace after the candidate + location.contents.slice(location.end, location.end + 2)?.match(/^:\s/) && + // A `` is present after the candidate + location.contents.slice(location.end).includes('') + ) { + return false + } + let [candidate] = Array.from(parseCandidate(rawCandidate, designSystem)) // If we can't parse the candidate, then it's not a candidate at all. However,