@@ -201,6 +201,7 @@ function applyVariant(variant, matches, context) {
201
201
}
202
202
203
203
if ( context . variantMap . has ( variant ) ) {
204
+ let isArbitraryVariant = isArbitraryValue ( variant )
204
205
let variantFunctionTuples = context . variantMap . get ( variant ) . slice ( )
205
206
let result = [ ]
206
207
@@ -262,7 +263,10 @@ function applyVariant(variant, matches, context) {
262
263
clone . append ( wrapper )
263
264
} ,
264
265
format ( selectorFormat ) {
265
- collectedFormats . push ( selectorFormat )
266
+ collectedFormats . push ( {
267
+ format : selectorFormat ,
268
+ isArbitraryVariant,
269
+ } )
266
270
} ,
267
271
args,
268
272
} )
@@ -288,7 +292,10 @@ function applyVariant(variant, matches, context) {
288
292
}
289
293
290
294
if ( typeof ruleWithVariant === 'string' ) {
291
- collectedFormats . push ( ruleWithVariant )
295
+ collectedFormats . push ( {
296
+ format : ruleWithVariant ,
297
+ isArbitraryVariant,
298
+ } )
292
299
}
293
300
294
301
if ( ruleWithVariant === null ) {
@@ -329,7 +336,10 @@ function applyVariant(variant, matches, context) {
329
336
// modified (by plugin): .foo .foo\\:markdown > p
330
337
// rebuiltBase (internal): .foo\\:markdown > p
331
338
// format: .foo &
332
- collectedFormats . push ( modified . replace ( rebuiltBase , '&' ) )
339
+ collectedFormats . push ( {
340
+ format : modified . replace ( rebuiltBase , '&' ) ,
341
+ isArbitraryVariant,
342
+ } )
333
343
rule . selector = before
334
344
} )
335
345
}
@@ -349,7 +359,6 @@ function applyVariant(variant, matches, context) {
349
359
Object . assign ( args , context . variantOptions . get ( variant ) )
350
360
) ,
351
361
collectedFormats : ( meta . collectedFormats ?? [ ] ) . concat ( collectedFormats ) ,
352
- isArbitraryVariant : isArbitraryValue ( variant ) ,
353
362
} ,
354
363
clone . nodes [ 0 ] ,
355
364
]
@@ -733,48 +742,15 @@ function* resolveMatches(candidate, context, original = candidate) {
733
742
}
734
743
735
744
for ( let match of matches ) {
736
- let isValid = true
737
-
738
745
match [ 1 ] . raws . tailwind = { ...match [ 1 ] . raws . tailwind , candidate }
739
746
740
747
// Apply final format selector
741
- if ( match [ 0 ] . collectedFormats ) {
742
- let finalFormat = formatVariantSelector ( '&' , ...match [ 0 ] . collectedFormats )
743
- let container = postcss . root ( { nodes : [ match [ 1 ] . clone ( ) ] } )
744
- container . walkRules ( ( rule ) => {
745
- if ( inKeyframes ( rule ) ) return
746
-
747
- let selectorOptions = {
748
- selector : rule . selector ,
749
- candidate : original ,
750
- base : candidate
751
- . split ( new RegExp ( `\\${ context ?. tailwindConfig ?. separator ?? ':' } (?![^[]*\\])` ) )
752
- . pop ( ) ,
753
- isArbitraryVariant : match [ 0 ] . isArbitraryVariant ,
754
-
755
- context,
756
- }
757
-
758
- try {
759
- rule . selector = finalizeSelector ( finalFormat , selectorOptions )
760
- } catch {
761
- // The selector we produced is invalid
762
- // This could be because:
763
- // - A bug exists
764
- // - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
765
- // - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
766
- // Either way the build will fail because of this
767
- // We would rather that the build pass "silently" given that this could
768
- // happen because of picking up invalid things when scanning content
769
- // So we'll throw out the candidate instead
770
- isValid = false
771
- return false
772
- }
773
- } )
774
- match [ 1 ] = container . nodes [ 0 ]
775
- }
748
+ match = applyFinalFormat ( match , { context, candidate, original } )
776
749
777
- if ( ! isValid ) {
750
+ // Skip rules with invalid selectors
751
+ // This will cause the candidate to be added to the "not class"
752
+ // cache skipping it entirely for future builds
753
+ if ( match === null ) {
778
754
continue
779
755
}
780
756
@@ -783,6 +759,62 @@ function* resolveMatches(candidate, context, original = candidate) {
783
759
}
784
760
}
785
761
762
+ function applyFinalFormat ( match , { context, candidate, original } ) {
763
+ if ( ! match [ 0 ] . collectedFormats ) {
764
+ return match
765
+ }
766
+
767
+ let isValid = true
768
+ let finalFormat
769
+
770
+ try {
771
+ finalFormat = formatVariantSelector ( match [ 0 ] . collectedFormats , {
772
+ context,
773
+ candidate,
774
+ } )
775
+ } catch {
776
+ // The format selector we produced is invalid
777
+ // This could be because:
778
+ // - A bug exists
779
+ // - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
780
+ // - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
781
+ // Either way the build will fail because of this
782
+ // We would rather that the build pass "silently" given that this could
783
+ // happen because of picking up invalid things when scanning content
784
+ // So we'll throw out the candidate instead
785
+
786
+ return null
787
+ }
788
+
789
+ let container = postcss . root ( { nodes : [ match [ 1 ] . clone ( ) ] } )
790
+
791
+ container . walkRules ( ( rule ) => {
792
+ if ( inKeyframes ( rule ) ) {
793
+ return
794
+ }
795
+
796
+ try {
797
+ rule . selector = finalizeSelector ( rule . selector , finalFormat , {
798
+ candidate : original ,
799
+ context,
800
+ } )
801
+ } catch {
802
+ // If this selector is invalid we also want to skip it
803
+ // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
804
+ isValid = false
805
+ return false
806
+ }
807
+ } )
808
+
809
+ if ( ! isValid ) {
810
+ return null
811
+ }
812
+
813
+ match [ 1 ] = container . nodes [ 0 ]
814
+
815
+ return match
816
+ }
817
+
786
818
function inKeyframes ( rule ) {
787
819
return rule . parent && rule . parent . type === 'atrule' && rule . parent . name === 'keyframes'
788
820
}
0 commit comments