@@ -3,7 +3,7 @@ import selectorParser from 'postcss-selector-parser'
3
3
import parseObjectStyles from '../util/parseObjectStyles'
4
4
import isPlainObject from '../util/isPlainObject'
5
5
import prefixSelector from '../util/prefixSelector'
6
- import { updateAllClasses } from '../util/pluginUtils'
6
+ import { updateAllClasses , typeMap } from '../util/pluginUtils'
7
7
import log from '../util/log'
8
8
import * as sharedState from './sharedState'
9
9
import { formatVariantSelector , finalizeSelector } from '../util/formatVariantSelector'
@@ -535,15 +535,28 @@ function* resolveMatches(candidate, context, original = candidate) {
535
535
}
536
536
537
537
if ( matchesPerPlugin . length > 0 ) {
538
- typesByMatches . set ( matchesPerPlugin , sort . options ?. type )
538
+ let matchingTypes = ( sort . options ?. types ?? [ ] )
539
+ . map ( ( { type } ) => type )
540
+ . filter ( ( type ) => {
541
+ return Boolean (
542
+ typeMap [ type ] ( modifier , sort . options , {
543
+ tailwindConfig : context . tailwindConfig ,
544
+ } )
545
+ )
546
+ } )
547
+
548
+ if ( matchingTypes . length > 0 ) {
549
+ typesByMatches . set ( matchesPerPlugin , matchingTypes )
550
+ }
551
+
539
552
matches . push ( matchesPerPlugin )
540
553
}
541
554
}
542
555
543
556
if ( isArbitraryValue ( modifier ) ) {
544
- // When generated arbitrary values are ambiguous, we can't know
545
- // which to pick so don't generate any utilities for them
546
557
if ( matches . length > 1 ) {
558
+ // When generated arbitrary values are ambiguous, we can't know which to pick so don't
559
+ // generate any utilities for them
547
560
let typesPerPlugin = matches . map ( ( match ) => new Set ( [ ...( typesByMatches . get ( match ) ?? [ ] ) ] ) )
548
561
549
562
// Remove duplicates, so that we can detect proper unique types for each plugin.
@@ -564,39 +577,81 @@ function* resolveMatches(candidate, context, original = candidate) {
564
577
}
565
578
}
566
579
567
- let messages = [ ]
568
-
569
- for ( let [ idx , group ] of typesPerPlugin . entries ( ) ) {
570
- for ( let type of group ) {
571
- let rules = matches [ idx ]
572
- . map ( ( [ , rule ] ) => rule )
573
- . flat ( )
574
- . map ( ( rule ) =>
575
- rule
576
- . toString ( )
577
- . split ( '\n' )
578
- . slice ( 1 , - 1 ) // Remove selector and closing ' }'
579
- . map ( ( line ) => line . trim ( ) )
580
- . map ( ( x ) => ` ${ x } ` ) // Re-indent
581
- . join ( '\n' )
582
- )
583
- . join ( '\n\n' )
584
-
585
- messages . push (
586
- ` Use \`${ candidate . replace ( '[' , `[${ type } :` ) } \` for \`${ rules . trim ( ) } \``
580
+ // Partition
581
+ let [ withAny , withoutAny ] = matches . reduce (
582
+ ( group , plugin ) => {
583
+ let hasAnyType = plugin . some ( ( [ { options } ] ) =>
584
+ options . types . some ( ( { type } ) => type === 'any' )
587
585
)
588
- break
586
+
587
+ if ( hasAnyType ) {
588
+ group [ 0 ] . push ( plugin )
589
+ } else {
590
+ group [ 1 ] . push ( plugin )
591
+ }
592
+ return group
593
+ } ,
594
+ [ [ ] , [ ] ]
595
+ )
596
+
597
+ function findFallback ( matches ) {
598
+ if ( matches . length === 1 ) {
599
+ return matches [ 0 ]
589
600
}
601
+
602
+ return matches . find ( ( rules ) => {
603
+ let matchingTypes = typesByMatches . get ( rules )
604
+ return rules . some ( ( [ { options } , rule ] ) => {
605
+ if ( ! isParsableNode ( rule ) ) {
606
+ return false
607
+ }
608
+
609
+ return options . types . some (
610
+ ( { type, disambiguate } ) => matchingTypes . includes ( type ) && disambiguate
611
+ )
612
+ } )
613
+ } )
590
614
}
591
615
592
- log . warn ( [
593
- `The class \`${ candidate } \` is ambiguous and matches multiple utilities.` ,
594
- ...messages ,
595
- `If this is content and not a class, replace it with \`${ candidate
596
- . replace ( '[' , '[' )
597
- . replace ( ']' , ']' ) } \` to silence this warning.`,
598
- ] )
599
- continue
616
+ let fallback = findFallback ( withoutAny ) ?? findFallback ( withAny )
617
+
618
+ if ( fallback ) {
619
+ matches = [ fallback ]
620
+ } else {
621
+ let messages = [ ]
622
+
623
+ for ( let [ idx , group ] of typesPerPlugin . entries ( ) ) {
624
+ for ( let type of group ) {
625
+ let rules = matches [ idx ]
626
+ . map ( ( [ , rule ] ) => rule )
627
+ . flat ( )
628
+ . map ( ( rule ) =>
629
+ rule
630
+ . toString ( )
631
+ . split ( '\n' )
632
+ . slice ( 1 , - 1 ) // Remove selector and closing ' }'
633
+ . map ( ( line ) => line . trim ( ) )
634
+ . map ( ( x ) => ` ${ x } ` ) // Re-indent
635
+ . join ( '\n' )
636
+ )
637
+ . join ( '\n\n' )
638
+
639
+ messages . push (
640
+ ` Use \`${ candidate . replace ( '[' , `[${ type } :` ) } \` for \`${ rules . trim ( ) } \``
641
+ )
642
+ break
643
+ }
644
+ }
645
+
646
+ log . warn ( [
647
+ `The class \`${ candidate } \` is ambiguous and matches multiple utilities.` ,
648
+ ...messages ,
649
+ `If this is content and not a class, replace it with \`${ candidate
650
+ . replace ( '[' , '[' )
651
+ . replace ( ']' , ']' ) } \` to silence this warning.`,
652
+ ] )
653
+ continue
654
+ }
600
655
}
601
656
602
657
matches = matches . map ( ( list ) => list . filter ( ( match ) => isParsableNode ( match [ 1 ] ) ) )
0 commit comments