Skip to content

Commit

Permalink
Upgrade: Do not extract class names from functions (e.g. shadow in …
Browse files Browse the repository at this point in the history
…`filter: 'drop-shadow(…)'`)
  • Loading branch information
philipp-spiess committed Jan 7, 2025
1 parent c84acf8 commit b28e9bb
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 20 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix `inset-shadow-*` suggestions in IntelliSense ([#15471](https://github.com/tailwindlabs/tailwindcss/pull/15471))
- Only compile arbitrary values ending in `]` ([#15503](https://github.com/tailwindlabs/tailwindcss/pull/15503))
- Improve performance and memory usage ([#15529](https://github.com/tailwindlabs/tailwindcss/pull/15529))
- _Upgrade (experimental)_: Do not extract class names from functions (e.g. `shadow` in `filter: 'drop-shadow(…)'`) ([#15566](https://github.com/tailwindlabs/tailwindcss/pull/15566))

### Changed

Expand Down Expand Up @@ -782,4 +783,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [4.0.0-alpha.1] - 2024-03-06

- First 4.0.0-alpha.1 release

2 changes: 2 additions & 0 deletions integrations/upgrade/js-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ test(
'src/test.js': ts`
export default {
'shouldNotMigrate': !border.test + '',
'filter': 'drop-shadow(0 0 0.5rem #000)',
}
`,
'src/index.html': html`
Expand Down Expand Up @@ -288,6 +289,7 @@ test(
--- src/test.js ---
export default {
'shouldNotMigrate': !border.test + '',
'filter': 'drop-shadow(0 0 0.5rem #000)',
}
"
`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,31 @@ test('does not match false positives', async () => {
).toEqual('!border')
})

test('does not match false positives', async () => {
test('does not replace classes in invalid positions', async () => {
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
base: __dirname,
})

function shouldNotDetect(example: string, candidate = '!border') {
function shouldNotReplace(example: string, candidate = '!border') {
expect(
important(designSystem, {}, candidate, {
contents: example,
start: example.indexOf(candidate),
end: example.indexOf(candidate) + candidate.length,
}),
).toEqual('!border')
).toEqual(candidate)
}

shouldNotDetect(`let notBorder = !border \n`)
shouldNotDetect(`{ "foo": !border.something + ""}\n`)
shouldNotDetect(`<div v-if="something && !border"></div>\n`)
shouldNotDetect(`<div v-else-if="something && !border"></div>\n`)
shouldNotDetect(`<div v-show="something && !border"></div>\n`)
shouldNotDetect(`<div v-if="!border || !border"></div>\n`)
shouldNotDetect(`<div v-else-if="!border || !border"></div>\n`)
shouldNotDetect(`<div v-show="!border || !border"></div>\n`)
shouldNotDetect(`<div v-if="!border"></div>\n`)
shouldNotDetect(`<div v-else-if="!border"></div>\n`)
shouldNotDetect(`<div v-show="!border"></div>\n`)
shouldNotDetect(`<div x-if="!border"></div>\n`)
shouldNotReplace(`let notBorder = !border \n`)
shouldNotReplace(`{ "foo": !border.something + ""}\n`)
shouldNotReplace(`<div v-if="something && !border"></div>\n`)
shouldNotReplace(`<div v-else-if="something && !border"></div>\n`)
shouldNotReplace(`<div v-show="something && !border"></div>\n`)
shouldNotReplace(`<div v-if="!border || !border"></div>\n`)
shouldNotReplace(`<div v-else-if="!border || !border"></div>\n`)
shouldNotReplace(`<div v-show="!border || !border"></div>\n`)
shouldNotReplace(`<div v-if="!border"></div>\n`)
shouldNotReplace(`<div v-else-if="!border"></div>\n`)
shouldNotReplace(`<div v-show="!border"></div>\n`)
shouldNotReplace(`<div x-if="!border"></div>\n`)
})
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,33 @@ test.each([

expect(await legacyClasses(designSystem, {}, candidate)).toEqual(result)
})

test('does not replace classes in invalid positions', async () => {
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
base: __dirname,
})

async function shouldNotReplace(example: string, candidate = 'shadow') {
expect(
await legacyClasses(designSystem, {}, candidate, {
contents: example,
start: example.indexOf(candidate),
end: example.indexOf(candidate) + candidate.length,
}),
).toEqual(candidate)
}

await shouldNotReplace(`let notShadow = shadow \n`)
await shouldNotReplace(`{ "foo": shadow.something + ""}\n`)
await shouldNotReplace(`<div v-if="something && shadow"></div>\n`)
await shouldNotReplace(`<div v-else-if="something && shadow"></div>\n`)
await shouldNotReplace(`<div v-show="something && shadow"></div>\n`)
await shouldNotReplace(`<div v-if="shadow || shadow"></div>\n`)
await shouldNotReplace(`<div v-else-if="shadow || shadow"></div>\n`)
await shouldNotReplace(`<div v-show="shadow || shadow"></div>\n`)
await shouldNotReplace(`<div v-if="shadow"></div>\n`)
await shouldNotReplace(`<div v-else-if="shadow"></div>\n`)
await shouldNotReplace(`<div v-show="shadow"></div>\n`)
await shouldNotReplace(`<div x-if="shadow"></div>\n`)
await shouldNotReplace(`<div style={{filter: 'drop-shadow(30px 10px 4px #4444dd);'}}\n`)
})
13 changes: 9 additions & 4 deletions packages/@tailwindcss-upgrade/src/template/is-safe-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,24 @@ export function isSafeMigration(location: { contents: string; start: number; end
currentLineAfterCandidate += char
}

// Heuristic 1: Require the candidate to be inside quotes
// Heuristic: Require the candidate to be inside quotes
let isQuoteBeforeCandidate = QUOTES.some((quote) => currentLineBeforeCandidate.includes(quote))
let isQuoteAfterCandidate = QUOTES.some((quote) => currentLineAfterCandidate.includes(quote))
if (!isQuoteBeforeCandidate || !isQuoteAfterCandidate) {
return false
}

// Heuristic 2: Disallow object access immediately following the candidate
// Heuristic: Disallow object access immediately following the candidate
if (currentLineAfterCandidate[0] === '.') {
return false
}

// Heuristic 3: Disallow logical operators preceding or following the candidate
// Heuristic: Disallow function call expressions immediately following the candidate
if (currentLineAfterCandidate.trim().startsWith('(')) {
return false
}

// Heuristic: Disallow logical operators preceding or following the candidate
for (let operator of LOGICAL_OPERATORS) {
if (
currentLineAfterCandidate.trim().startsWith(operator) ||
Expand All @@ -51,7 +56,7 @@ export function isSafeMigration(location: { contents: string; start: number; end
}
}

// Heuristic 4: Disallow conditional template syntax
// Heuristic: Disallow conditional template syntax
for (let rule of CONDITIONAL_TEMPLATE_SYNTAX) {
if (rule.test(currentLineBeforeCandidate)) {
return false
Expand Down

0 comments on commit b28e9bb

Please sign in to comment.