Discard matchVariant matches with unknown named values (#18799)
This PR fixes two issues:
- When a variant is defined by `matchVariant` it could match unknown
values but not apply the variant (because it's unknown). This would
result in a utility being output that is the _same_ as a bare utility
without variants but a longer name. These were intended to be discarded
but weren't done so correctly.
- Similarly, when we encounter a known value but its not a string the
same thing would happen where we'd output a utility without applying the
variant. This was also intended to be discarded.
Basically given this code:
```js
matchVariant(
"foo",
(value) => `&:is([data-foo='${value}'])`,
{
values: {
DEFAULT: "",
bar: "bar",
obj: { some: "object" },
},
}
)
```
And this HTML:
```html
<div class="foo-bar:bg-none foo-[baz]:bg-none foo-baz:bg-none foo-obj:bg-none"></div>
```
This CSS would be produced:
```css
@layer utilities {
.foo-bar\:bg-none {
&:is([data-foo='bar']) {
background-image: none;
}
}
/* this one shouldn't be here */
.foo-baz\:bg-none {
background-image: none;
}
/* this one shouldn't be here */
.foo-obj\:bg-none {
background-image: none;
}
.foo-\[baz\]\:bg-none {
&:is([data-foo='baz']) {
background-image: none;
}
}
}
```
This commit is contained in:
parent
ce9b290b6b
commit
ee987e3f6a
3 changed files with 78 additions and 1 deletions
|
|
@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Fixed
|
||||
|
||||
- Don't transition `visibility` when using `transition` ([#18795](https://github.com/tailwindlabs/tailwindcss/pull/18795))
|
||||
- Discard matched variants with unknown named values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
|
||||
- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
|
||||
|
||||
## [4.1.12] - 2025-08-13
|
||||
|
||||
|
|
|
|||
|
|
@ -2778,6 +2778,79 @@ describe('matchVariant', () => {
|
|||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('ignores variants that use unknown values', async () => {
|
||||
let { build } = await compile(
|
||||
css`
|
||||
@plugin "my-plugin";
|
||||
@layer utilities {
|
||||
@tailwind utilities;
|
||||
}
|
||||
`,
|
||||
{
|
||||
loadModule: async (id, base) => {
|
||||
return {
|
||||
path: '',
|
||||
base,
|
||||
module: ({ matchVariant }: PluginAPI) => {
|
||||
matchVariant('foo', (flavor) => `&:is(${flavor})`, {
|
||||
values: {
|
||||
known: 'known',
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
let compiled = build(['foo-[test]:flex', 'foo-known:flex', 'foo-unknown:flex'])
|
||||
|
||||
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
|
||||
"@layer utilities {
|
||||
.foo-known\\:flex:is(known), .foo-\\[test\\]\\:flex:is(test) {
|
||||
display: flex;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('ignores variants that produce non-string values', async () => {
|
||||
let { build } = await compile(
|
||||
css`
|
||||
@plugin "my-plugin";
|
||||
@layer utilities {
|
||||
@tailwind utilities;
|
||||
}
|
||||
`,
|
||||
{
|
||||
loadModule: async (id, base) => {
|
||||
return {
|
||||
path: '',
|
||||
base,
|
||||
module: ({ matchVariant }: PluginAPI) => {
|
||||
matchVariant('foo', (flavor) => `&:is(${flavor})`, {
|
||||
values: {
|
||||
string: 'some string',
|
||||
object: { some: 'object' },
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
let compiled = build(['foo-[test]:flex', 'foo-string:flex', 'foo-object:flex'])
|
||||
|
||||
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
|
||||
"@layer utilities {
|
||||
.foo-string\\:flex:is(some string), .foo-\\[test\\]\\:flex:is(test) {
|
||||
display: flex;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('addUtilities()', () => {
|
||||
|
|
|
|||
|
|
@ -203,10 +203,12 @@ export function buildPluginApi({
|
|||
} else if (variant.value.kind === 'named' && options?.values) {
|
||||
let defaultValue = options.values[variant.value.value]
|
||||
if (typeof defaultValue !== 'string') {
|
||||
return
|
||||
return null
|
||||
}
|
||||
|
||||
ruleNodes.nodes = resolveVariantValue(defaultValue, variant.modifier, ruleNodes.nodes)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue