Skip to content

Commit 6b5a2a9

Browse files
committed
Discard invalid variants such as data-foo-[bar=1]
1 parent b4a20af commit 6b5a2a9

File tree

2 files changed

+74
-7
lines changed

2 files changed

+74
-7
lines changed

packages/tailwindcss/src/candidate.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,39 @@ it('should not parse invalid arbitrary values', () => {
836836
}
837837
})
838838

839+
it('should not parse invalid arbitrary values in variants', () => {
840+
let utilities = new Utilities()
841+
utilities.static('flex', () => [])
842+
843+
let variants = new Variants()
844+
variants.functional('data', () => {})
845+
846+
for (let candidate of [
847+
'data-foo-[#0088cc]:flex',
848+
'data-foo[#0088cc]:flex',
849+
850+
'data-foo-[color:var(--value)]:flex',
851+
'data-foo[color:var(--value)]:flex',
852+
853+
'data-foo-[#0088cc]/50:flex',
854+
'data-foo[#0088cc]/50:flex',
855+
856+
'data-foo-[#0088cc]/[50%]:flex',
857+
'data-foo[#0088cc]/[50%]:flex',
858+
859+
'data-foo-[#0088cc]:flex!',
860+
'data-foo[#0088cc]:flex!',
861+
862+
'data-foo-[var(--value)]:flex',
863+
'data-foo[var(--value)]:flex',
864+
865+
'data-foo-[var(--value)]:flex!',
866+
'data-foo[var(--value)]:flex!',
867+
]) {
868+
expect(run(candidate, { utilities, variants })).toEqual([])
869+
}
870+
})
871+
839872
it('should parse a utility with an implicit variable as the modifier', () => {
840873
let utilities = new Utilities()
841874
utilities.functional('bg', () => [])
@@ -966,6 +999,18 @@ it('should parse a utility with an explicit variable as the modifier that is imp
966999
`)
9671000
})
9681001

1002+
it('should not parse a partial variant', () => {
1003+
let utilities = new Utilities()
1004+
utilities.static('flex', () => [])
1005+
1006+
let variants = new Variants()
1007+
variants.static('open', () => {})
1008+
variants.functional('data', () => {})
1009+
1010+
expect(run('open-:flex', { utilities, variants })).toMatchInlineSnapshot(`[]`)
1011+
expect(run('data-:flex', { utilities, variants })).toMatchInlineSnapshot(`[]`)
1012+
})
1013+
9691014
it('should parse a static variant starting with @', () => {
9701015
let utilities = new Utilities()
9711016
utilities.static('flex', () => [])

packages/tailwindcss/src/candidate.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ export function* parseCandidate(input: string, designSystem: DesignSystem): Iter
413413
// Not an arbitrary value
414414
else {
415415
roots = findRoots(baseWithoutModifier, (root: string) => {
416-
return designSystem.utilities.has(root, 'functional')
416+
return designSystem.utilities.has(root, 'functional') ? 'functional' : null
417417
})
418418
}
419419

@@ -587,7 +587,7 @@ export function parseVariant(variant: string, designSystem: DesignSystem): Varia
587587
if (additionalModifier) return null
588588

589589
let roots = findRoots(variantWithoutModifier, (root) => {
590-
return designSystem.variants.has(root)
590+
return designSystem.variants.has(root) ? designSystem.variants.kind(root) : null
591591
})
592592

593593
for (let [root, value] of roots) {
@@ -702,9 +702,12 @@ type Root = [
702702
value: string | null,
703703
]
704704

705-
function* findRoots(input: string, exists: (input: string) => boolean): Iterable<Root> {
705+
function* findRoots(
706+
input: string,
707+
kind: (input: string) => 'static' | 'functional' | 'compound' | 'arbitrary' | null,
708+
): Iterable<Root> {
706709
// If there is an exact match, then that's the root.
707-
if (exists(input)) {
710+
if (kind(input)) {
708711
yield [input, null]
709712
}
710713

@@ -714,7 +717,7 @@ function* findRoots(input: string, exists: (input: string) => boolean): Iterable
714717
if (idx === -1) {
715718
// Variants starting with `@` are special because they don't need a `-`
716719
// after the `@` (E.g.: `@-lg` should be written as `@lg`).
717-
if (input[0] === '@' && exists('@')) {
720+
if (input[0] === '@' && kind('@')) {
718721
yield ['@', input.slice(1)]
719722
}
720723
return
@@ -729,9 +732,28 @@ function* findRoots(input: string, exists: (input: string) => boolean): Iterable
729732
// `bg` -> Match
730733
do {
731734
let maybeRoot = input.slice(0, idx)
735+
let rootKind = kind(maybeRoot)
736+
737+
if (rootKind) {
738+
let value = input.slice(idx + 1)
739+
740+
// Compound variants e.g. not-in-[#foo] are ultimately split like so:
741+
// - root: not
742+
// - value: in-[#foo]
743+
// - root: in
744+
// - value: [#foo]
745+
//
746+
// However, other variants don't have this behavior, so we can skip
747+
// over this possible variant if the root is not a compound variant
748+
// and it contains an arbitrary value _after_ some other value
749+
// e.g. `supports-display-[color:red]` is invalid
750+
if (rootKind !== 'compound') {
751+
if (value[value.length - 1] === ']' && value[0] !== '[') {
752+
break
753+
}
754+
}
732755

733-
if (exists(maybeRoot)) {
734-
let root: Root = [maybeRoot, input.slice(idx + 1)]
756+
let root: Root = [maybeRoot, value]
735757

736758
// If the leftover value is an empty string, it means that the value is an
737759
// invalid named value, e.g.: `bg-`. This makes the candidate invalid and we

0 commit comments

Comments
 (0)