mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Fix bug replacing modifier variable shorthand syntax underscores (#17889)
Resolves #17888 **Reproduction URL:** https://play.tailwindcss.com/YvIekuzVRd Changes: * Don't use `decodeArbitraryValue` when parsing variable shorthand syntax in modifiers * replace `decodeArbitraryValue(modifier.slice(1, -1))` with `modifier.slice(1, -1)` * added test case, passing ✅ --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This commit is contained in:
parent
ed45952d15
commit
4f8539c063
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Ensure negative arbitrary `scale` values generate negative values ([#17831](https://github.com/tailwindlabs/tailwindcss/pull/17831))
|
||||
- Fix HAML extraction with embedded Ruby ([#17846](https://github.com/tailwindlabs/tailwindcss/pull/17846))
|
||||
- Don't scan files for utilities when using `@reference` ([#17836](https://github.com/tailwindlabs/tailwindcss/pull/17836))
|
||||
- Fix incorrectly replacing `_` with ` ` in arbitrary modifier shorthand `bg-red-500/(--my_opacity)` ([#17889](https://github.com/tailwindlabs/tailwindcss/pull/17889))
|
||||
|
||||
## [4.1.5] - 2025-04-30
|
||||
|
||||
|
||||
@ -1087,6 +1087,7 @@ it('should parse a utility with an implicit variable as the modifier using the s
|
||||
let utilities = new Utilities()
|
||||
utilities.functional('bg', () => [])
|
||||
|
||||
// Standard case (no underscores)
|
||||
expect(run('bg-red-500/(--value)', { utilities })).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
@ -1107,6 +1108,156 @@ it('should parse a utility with an implicit variable as the modifier using the s
|
||||
},
|
||||
]
|
||||
`)
|
||||
|
||||
// Should preserve underscores
|
||||
expect(run('bg-red-500/(--with_underscore)', { utilities })).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "functional",
|
||||
"modifier": {
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--with_underscore)",
|
||||
},
|
||||
"raw": "bg-red-500/(--with_underscore)",
|
||||
"root": "bg",
|
||||
"value": {
|
||||
"fraction": null,
|
||||
"kind": "named",
|
||||
"value": "red-500",
|
||||
},
|
||||
"variants": [],
|
||||
},
|
||||
]
|
||||
`)
|
||||
|
||||
// Should remove underscores in fallback values
|
||||
expect(run('bg-red-500/(--with_underscore,fallback_value)', { utilities }))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "functional",
|
||||
"modifier": {
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--with_underscore,fallback value)",
|
||||
},
|
||||
"raw": "bg-red-500/(--with_underscore,fallback_value)",
|
||||
"root": "bg",
|
||||
"value": {
|
||||
"fraction": null,
|
||||
"kind": "named",
|
||||
"value": "red-500",
|
||||
},
|
||||
"variants": [],
|
||||
},
|
||||
]
|
||||
`)
|
||||
|
||||
// Should keep underscores in the CSS variable itself, but remove underscores
|
||||
// in fallback values
|
||||
expect(run('bg-(--a_b,c_d_var(--e_f,g_h))/(--i_j,k_l_var(--m_n,o_p))', { utilities }))
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "functional",
|
||||
"modifier": {
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--i_j,k l var(--m_n,o p))",
|
||||
},
|
||||
"raw": "bg-(--a_b,c_d_var(--e_f,g_h))/(--i_j,k_l_var(--m_n,o_p))",
|
||||
"root": "bg",
|
||||
"value": {
|
||||
"dataType": null,
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--a_b,c d var(--e_f,g h))",
|
||||
},
|
||||
"variants": [],
|
||||
},
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should not parse an invalid arbitrary shorthand modifier', () => {
|
||||
let utilities = new Utilities()
|
||||
utilities.functional('bg', () => [])
|
||||
|
||||
// Completely empty
|
||||
expect(run('bg-red-500/()', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to leading spaces
|
||||
expect(run('bg-red-500/(_--)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-red-500/(_--x)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to leading spaces
|
||||
expect(run('bg-red-500/(_--)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-red-500/(_--x)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to top-level `;` or `}` characters
|
||||
expect(run('bg-red-500/(--x;--y)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-red-500/(--x:{foo:bar})', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Valid, but ensuring that we didn't make an off-by-one error
|
||||
expect(run('bg-red-500/(--x)', { utilities })).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "functional",
|
||||
"modifier": {
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--x)",
|
||||
},
|
||||
"raw": "bg-red-500/(--x)",
|
||||
"root": "bg",
|
||||
"value": {
|
||||
"fraction": null,
|
||||
"kind": "named",
|
||||
"value": "red-500",
|
||||
},
|
||||
"variants": [],
|
||||
},
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should not parse an invalid arbitrary shorthand value', () => {
|
||||
let utilities = new Utilities()
|
||||
utilities.functional('bg', () => [])
|
||||
|
||||
// Completely empty
|
||||
expect(run('bg-()', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to leading spaces
|
||||
expect(run('bg-(_--)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-(_--x)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to leading spaces
|
||||
expect(run('bg-(_--)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-(_--x)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Invalid due to top-level `;` or `}` characters
|
||||
expect(run('bg-(--x;--y)', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
expect(run('bg-(--x:{foo:bar})', { utilities })).toMatchInlineSnapshot(`[]`)
|
||||
|
||||
// Valid, but ensuring that we didn't make an off-by-one error
|
||||
expect(run('bg-(--x)', { utilities })).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"important": false,
|
||||
"kind": "functional",
|
||||
"modifier": null,
|
||||
"raw": "bg-(--x)",
|
||||
"root": "bg",
|
||||
"value": {
|
||||
"dataType": null,
|
||||
"kind": "arbitrary",
|
||||
"value": "var(--x)",
|
||||
},
|
||||
"variants": [],
|
||||
},
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should not parse a utility with an implicit invalid variable as the modifier using the shorthand', () => {
|
||||
|
||||
@ -411,7 +411,10 @@ export function* parseCandidate(input: string, designSystem: DesignSystem): Iter
|
||||
|
||||
// An arbitrary value with `(…)` should always start with `--` since it
|
||||
// represents a CSS variable.
|
||||
if (value[0] !== '-' && value[1] !== '-') return
|
||||
if (value[0] !== '-' || value[1] !== '-') return
|
||||
|
||||
// Values can't contain `;` or `}` characters at the top-level.
|
||||
if (!isValidArbitrary(value)) return
|
||||
|
||||
roots = [[root, dataType === null ? `[var(${value})]` : `[${dataType}:var(${value})]`]]
|
||||
}
|
||||
@ -523,21 +526,24 @@ function parseModifier(modifier: string): CandidateModifier | null {
|
||||
}
|
||||
|
||||
if (modifier[0] === '(' && modifier[modifier.length - 1] === ')') {
|
||||
let arbitraryValue = decodeArbitraryValue(modifier.slice(1, -1))
|
||||
// Drop the `(` and `)` characters
|
||||
modifier = modifier.slice(1, -1)
|
||||
|
||||
// A modifier with `(…)` should always start with `--` since it
|
||||
// represents a CSS variable.
|
||||
if (modifier[0] !== '-' || modifier[1] !== '-') return null
|
||||
|
||||
// Values can't contain `;` or `}` characters at the top-level.
|
||||
if (!isValidArbitrary(arbitraryValue)) return null
|
||||
if (!isValidArbitrary(modifier)) return null
|
||||
|
||||
// Empty arbitrary values are invalid. E.g.: `data-():`
|
||||
// ^^
|
||||
if (arbitraryValue.length === 0 || arbitraryValue.trim().length === 0) return null
|
||||
// Wrap the value in `var(…)` to ensure that it is a valid CSS variable.
|
||||
modifier = `var(${modifier})`
|
||||
|
||||
// Arbitrary values must start with `--` since it represents a CSS variable.
|
||||
if (arbitraryValue[0] !== '-' && arbitraryValue[1] !== '-') return null
|
||||
let arbitraryValue = decodeArbitraryValue(modifier)
|
||||
|
||||
return {
|
||||
kind: 'arbitrary',
|
||||
value: `var(${arbitraryValue})`,
|
||||
value: arbitraryValue,
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,7 +685,7 @@ export function parseVariant(variant: string, designSystem: DesignSystem): Varia
|
||||
if (arbitraryValue.length === 0 || arbitraryValue.trim().length === 0) return null
|
||||
|
||||
// Arbitrary values must start with `--` since it represents a CSS variable.
|
||||
if (arbitraryValue[0] !== '-' && arbitraryValue[1] !== '-') return null
|
||||
if (arbitraryValue[0] !== '-' || arbitraryValue[1] !== '-') return null
|
||||
|
||||
return {
|
||||
kind: 'functional',
|
||||
@ -1030,7 +1036,7 @@ function recursivelyEscapeUnderscores(ast: ValueParser.ValueAstNode[]) {
|
||||
case 'word': {
|
||||
// Dashed idents and variables `var(--my-var)` and `--my-var` should not
|
||||
// have underscores escaped
|
||||
if (node.value[0] !== '-' && node.value[1] !== '-') {
|
||||
if (node.value[0] !== '-' || node.value[1] !== '-') {
|
||||
node.value = escapeUnderscore(node.value)
|
||||
}
|
||||
break
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user