@@ -289,6 +289,20 @@ function isInPseudoClass(selector: selectorParser.Node): boolean {
289
289
) ;
290
290
}
291
291
292
+ /**
293
+ * Returns true if the selector is inside the pseudo classes :where() or :is()
294
+ * @param selector - selector
295
+ */
296
+ function isInPseudoClassWhereOrIs ( selector : selectorParser . Node ) : boolean {
297
+ return (
298
+ ( selector . parent &&
299
+ selector . parent . type === "pseudo" &&
300
+ ( selector . parent . value === ":where" ||
301
+ selector . parent . value === ":is" ) ) ||
302
+ false
303
+ ) ;
304
+ }
305
+
292
306
function isPostCSSAtRule ( node ?: postcss . Node ) : node is postcss . AtRule {
293
307
return node ?. type === "atrule" ;
294
308
}
@@ -511,6 +525,13 @@ class PurgeCSS {
511
525
512
526
let keepSelector = true ;
513
527
const selectorsRemovedFromRule : string [ ] = [ ] ;
528
+
529
+ // selector transformer, walk over the list of the parsed selectors twice.
530
+ // First pass will remove the unused selectors. It goes through
531
+ // pseudo-classes like :where() and :is() and remove the unused
532
+ // selectors inside of them, but will not remove the pseudo-classes
533
+ // themselves. Second pass will remove selectors containing empty
534
+ // :where and :is.
514
535
node . selector = selectorParser ( ( selectorsParsed ) => {
515
536
selectorsParsed . walk ( ( selector ) => {
516
537
if ( selector . type !== "selector" ) {
@@ -529,6 +550,19 @@ class PurgeCSS {
529
550
selector . remove ( ) ;
530
551
}
531
552
} ) ;
553
+
554
+ selectorsParsed . walk ( ( selector ) => {
555
+ if ( selector . type !== "selector" ) {
556
+ return ;
557
+ }
558
+
559
+ if (
560
+ selector . toString ( ) &&
561
+ / ( : w h e r e $ ) | ( : i s $ ) | ( : w h e r e [ ^ ( ] ) | ( : i s [ ^ ( ] ) / . test ( selector . toString ( ) )
562
+ ) {
563
+ selector . remove ( ) ;
564
+ }
565
+ } ) ;
532
566
} ) . processSync ( node . selector ) ;
533
567
534
568
// declarations
@@ -815,8 +849,10 @@ class PurgeCSS {
815
849
selector : selectorParser . Selector ,
816
850
selectorsFromExtractor : ExtractorResultSets
817
851
) : boolean {
818
- // ignore the selector if it is inside a pseudo class
819
- if ( isInPseudoClass ( selector ) ) return true ;
852
+ // selectors in pseudo classes are ignored except :where() and :is(). For those pseudo-classes, we are treating the selectors inside the same way as they would be outside.
853
+ if ( isInPseudoClass ( selector ) && ! isInPseudoClassWhereOrIs ( selector ) ) {
854
+ return true ;
855
+ }
820
856
821
857
// if there is any greedy safelist pattern, run all the selector parts through them
822
858
// if there is any match, return true
0 commit comments