Skip to content

Commit b28e9bb

Browse files
Upgrade: Do not extract class names from functions (e.g. shadow in filter: 'drop-shadow(…)')
1 parent c84acf8 commit b28e9bb

File tree

5 files changed

+57
-20
lines changed

5 files changed

+57
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Fix `inset-shadow-*` suggestions in IntelliSense ([#15471](https://github.com/tailwindlabs/tailwindcss/pull/15471))
1616
- Only compile arbitrary values ending in `]` ([#15503](https://github.com/tailwindlabs/tailwindcss/pull/15503))
1717
- Improve performance and memory usage ([#15529](https://github.com/tailwindlabs/tailwindcss/pull/15529))
18+
- _Upgrade (experimental)_: Do not extract class names from functions (e.g. `shadow` in `filter: 'drop-shadow(…)'`) ([#15566](https://github.com/tailwindlabs/tailwindcss/pull/15566))
1819

1920
### Changed
2021

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

784785
- First 4.0.0-alpha.1 release
785-

integrations/upgrade/js-config.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ test(
137137
'src/test.js': ts`
138138
export default {
139139
'shouldNotMigrate': !border.test + '',
140+
'filter': 'drop-shadow(0 0 0.5rem #000)',
140141
}
141142
`,
142143
'src/index.html': html`
@@ -288,6 +289,7 @@ test(
288289
--- src/test.js ---
289290
export default {
290291
'shouldNotMigrate': !border.test + '',
292+
'filter': 'drop-shadow(0 0 0.5rem #000)',
291293
}
292294
"
293295
`)

packages/@tailwindcss-upgrade/src/template/codemods/important.test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,31 @@ test('does not match false positives', async () => {
3838
).toEqual('!border')
3939
})
4040

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

46-
function shouldNotDetect(example: string, candidate = '!border') {
46+
function shouldNotReplace(example: string, candidate = '!border') {
4747
expect(
4848
important(designSystem, {}, candidate, {
4949
contents: example,
5050
start: example.indexOf(candidate),
5151
end: example.indexOf(candidate) + candidate.length,
5252
}),
53-
).toEqual('!border')
53+
).toEqual(candidate)
5454
}
5555

56-
shouldNotDetect(`let notBorder = !border \n`)
57-
shouldNotDetect(`{ "foo": !border.something + ""}\n`)
58-
shouldNotDetect(`<div v-if="something && !border"></div>\n`)
59-
shouldNotDetect(`<div v-else-if="something && !border"></div>\n`)
60-
shouldNotDetect(`<div v-show="something && !border"></div>\n`)
61-
shouldNotDetect(`<div v-if="!border || !border"></div>\n`)
62-
shouldNotDetect(`<div v-else-if="!border || !border"></div>\n`)
63-
shouldNotDetect(`<div v-show="!border || !border"></div>\n`)
64-
shouldNotDetect(`<div v-if="!border"></div>\n`)
65-
shouldNotDetect(`<div v-else-if="!border"></div>\n`)
66-
shouldNotDetect(`<div v-show="!border"></div>\n`)
67-
shouldNotDetect(`<div x-if="!border"></div>\n`)
56+
shouldNotReplace(`let notBorder = !border \n`)
57+
shouldNotReplace(`{ "foo": !border.something + ""}\n`)
58+
shouldNotReplace(`<div v-if="something && !border"></div>\n`)
59+
shouldNotReplace(`<div v-else-if="something && !border"></div>\n`)
60+
shouldNotReplace(`<div v-show="something && !border"></div>\n`)
61+
shouldNotReplace(`<div v-if="!border || !border"></div>\n`)
62+
shouldNotReplace(`<div v-else-if="!border || !border"></div>\n`)
63+
shouldNotReplace(`<div v-show="!border || !border"></div>\n`)
64+
shouldNotReplace(`<div v-if="!border"></div>\n`)
65+
shouldNotReplace(`<div v-else-if="!border"></div>\n`)
66+
shouldNotReplace(`<div v-show="!border"></div>\n`)
67+
shouldNotReplace(`<div x-if="!border"></div>\n`)
6868
})

packages/@tailwindcss-upgrade/src/template/codemods/legacy-classes.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,33 @@ test.each([
3939

4040
expect(await legacyClasses(designSystem, {}, candidate)).toEqual(result)
4141
})
42+
43+
test('does not replace classes in invalid positions', async () => {
44+
let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
45+
base: __dirname,
46+
})
47+
48+
async function shouldNotReplace(example: string, candidate = 'shadow') {
49+
expect(
50+
await legacyClasses(designSystem, {}, candidate, {
51+
contents: example,
52+
start: example.indexOf(candidate),
53+
end: example.indexOf(candidate) + candidate.length,
54+
}),
55+
).toEqual(candidate)
56+
}
57+
58+
await shouldNotReplace(`let notShadow = shadow \n`)
59+
await shouldNotReplace(`{ "foo": shadow.something + ""}\n`)
60+
await shouldNotReplace(`<div v-if="something && shadow"></div>\n`)
61+
await shouldNotReplace(`<div v-else-if="something && shadow"></div>\n`)
62+
await shouldNotReplace(`<div v-show="something && shadow"></div>\n`)
63+
await shouldNotReplace(`<div v-if="shadow || shadow"></div>\n`)
64+
await shouldNotReplace(`<div v-else-if="shadow || shadow"></div>\n`)
65+
await shouldNotReplace(`<div v-show="shadow || shadow"></div>\n`)
66+
await shouldNotReplace(`<div v-if="shadow"></div>\n`)
67+
await shouldNotReplace(`<div v-else-if="shadow"></div>\n`)
68+
await shouldNotReplace(`<div v-show="shadow"></div>\n`)
69+
await shouldNotReplace(`<div x-if="shadow"></div>\n`)
70+
await shouldNotReplace(`<div style={{filter: 'drop-shadow(30px 10px 4px #4444dd);'}}\n`)
71+
})

packages/@tailwindcss-upgrade/src/template/is-safe-migration.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,24 @@ export function isSafeMigration(location: { contents: string; start: number; end
2929
currentLineAfterCandidate += char
3030
}
3131

32-
// Heuristic 1: Require the candidate to be inside quotes
32+
// Heuristic: Require the candidate to be inside quotes
3333
let isQuoteBeforeCandidate = QUOTES.some((quote) => currentLineBeforeCandidate.includes(quote))
3434
let isQuoteAfterCandidate = QUOTES.some((quote) => currentLineAfterCandidate.includes(quote))
3535
if (!isQuoteBeforeCandidate || !isQuoteAfterCandidate) {
3636
return false
3737
}
3838

39-
// Heuristic 2: Disallow object access immediately following the candidate
39+
// Heuristic: Disallow object access immediately following the candidate
4040
if (currentLineAfterCandidate[0] === '.') {
4141
return false
4242
}
4343

44-
// Heuristic 3: Disallow logical operators preceding or following the candidate
44+
// Heuristic: Disallow function call expressions immediately following the candidate
45+
if (currentLineAfterCandidate.trim().startsWith('(')) {
46+
return false
47+
}
48+
49+
// Heuristic: Disallow logical operators preceding or following the candidate
4550
for (let operator of LOGICAL_OPERATORS) {
4651
if (
4752
currentLineAfterCandidate.trim().startsWith(operator) ||
@@ -51,7 +56,7 @@ export function isSafeMigration(location: { contents: string; start: number; end
5156
}
5257
}
5358

54-
// Heuristic 4: Disallow conditional template syntax
59+
// Heuristic: Disallow conditional template syntax
5560
for (let rule of CONDITIONAL_TEMPLATE_SYNTAX) {
5661
if (rule.test(currentLineBeforeCandidate)) {
5762
return false

0 commit comments

Comments
 (0)