@@ -838,6 +838,173 @@ describe('Parser', () => {
838838 } )
839839 } )
840840
841+ describe ( 'vendor prefix detection for selectors' , ( ) => {
842+ test ( 'should detect -webkit- vendor prefix in pseudo-class' , ( ) => {
843+ let source = 'input:-webkit-autofill { color: black; }'
844+ let parser = new Parser ( source )
845+ let root = parser . parse ( )
846+
847+ let rule = root . first_child !
848+ let selector = rule . first_child !
849+ // Selector has detailed parsing enabled by default
850+ expect ( selector . has_children ) . toBe ( true )
851+ // Navigate: selector -> type selector (input) -> pseudo-class (next sibling)
852+ let typeSelector = selector . first_child !
853+ let pseudoClass = typeSelector . next_sibling !
854+ expect ( pseudoClass . name ) . toBe ( '-webkit-autofill' )
855+ expect ( pseudoClass . is_vendor_prefixed ) . toBe ( true )
856+ } )
857+
858+ test ( 'should detect -moz- vendor prefix in pseudo-class' , ( ) => {
859+ let source = 'button:-moz-focusring { outline: 2px solid blue; }'
860+ let parser = new Parser ( source )
861+ let root = parser . parse ( )
862+
863+ let rule = root . first_child !
864+ let selector = rule . first_child !
865+ let typeSelector = selector . first_child !
866+ let pseudoClass = typeSelector . next_sibling !
867+ expect ( pseudoClass . name ) . toBe ( '-moz-focusring' )
868+ expect ( pseudoClass . is_vendor_prefixed ) . toBe ( true )
869+ } )
870+
871+ test ( 'should detect -ms- vendor prefix in pseudo-class' , ( ) => {
872+ let source = 'input:-ms-input-placeholder { color: gray; }'
873+ let parser = new Parser ( source )
874+ let root = parser . parse ( )
875+
876+ let rule = root . first_child !
877+ let selector = rule . first_child !
878+ let typeSelector = selector . first_child !
879+ let pseudoClass = typeSelector . next_sibling !
880+ expect ( pseudoClass . name ) . toBe ( '-ms-input-placeholder' )
881+ expect ( pseudoClass . is_vendor_prefixed ) . toBe ( true )
882+ } )
883+
884+ test ( 'should detect -webkit- vendor prefix in pseudo-element' , ( ) => {
885+ let source = 'div::-webkit-scrollbar { width: 10px; }'
886+ let parser = new Parser ( source )
887+ let root = parser . parse ( )
888+
889+ let rule = root . first_child !
890+ let selector = rule . first_child !
891+ let typeSelector = selector . first_child !
892+ let pseudoElement = typeSelector . next_sibling !
893+ expect ( pseudoElement . name ) . toBe ( '-webkit-scrollbar' )
894+ expect ( pseudoElement . is_vendor_prefixed ) . toBe ( true )
895+ } )
896+
897+ test ( 'should detect -moz- vendor prefix in pseudo-element' , ( ) => {
898+ let source = 'div::-moz-selection { background: yellow; }'
899+ let parser = new Parser ( source )
900+ let root = parser . parse ( )
901+
902+ let rule = root . first_child !
903+ let selector = rule . first_child !
904+ let typeSelector = selector . first_child !
905+ let pseudoElement = typeSelector . next_sibling !
906+ expect ( pseudoElement . name ) . toBe ( '-moz-selection' )
907+ expect ( pseudoElement . is_vendor_prefixed ) . toBe ( true )
908+ } )
909+
910+ test ( 'should detect -webkit- vendor prefix in pseudo-element with multiple parts' , ( ) => {
911+ let source = 'input::-webkit-input-placeholder { color: gray; }'
912+ let parser = new Parser ( source )
913+ let root = parser . parse ( )
914+
915+ let rule = root . first_child !
916+ let selector = rule . first_child !
917+ let typeSelector = selector . first_child !
918+ let pseudoElement = typeSelector . next_sibling !
919+ expect ( pseudoElement . name ) . toBe ( '-webkit-input-placeholder' )
920+ expect ( pseudoElement . is_vendor_prefixed ) . toBe ( true )
921+ } )
922+
923+ test ( 'should detect -webkit- vendor prefix in pseudo-class function' , ( ) => {
924+ let source = 'input:-webkit-any(input, button) { margin: 0; }'
925+ let parser = new Parser ( source )
926+ let root = parser . parse ( )
927+
928+ let rule = root . first_child !
929+ let selector = rule . first_child !
930+ let typeSelector = selector . first_child !
931+ let pseudoClass = typeSelector . next_sibling !
932+ expect ( pseudoClass . name ) . toBe ( '-webkit-any' )
933+ expect ( pseudoClass . is_vendor_prefixed ) . toBe ( true )
934+ } )
935+
936+ test ( 'should not detect vendor prefix for standard pseudo-classes' , ( ) => {
937+ let source = 'a:hover { color: blue; }'
938+ let parser = new Parser ( source )
939+ let root = parser . parse ( )
940+
941+ let rule = root . first_child !
942+ let selector = rule . first_child !
943+ let typeSelector = selector . first_child !
944+ let pseudoClass = typeSelector . next_sibling !
945+ expect ( pseudoClass . name ) . toBe ( 'hover' )
946+ expect ( pseudoClass . is_vendor_prefixed ) . toBe ( false )
947+ } )
948+
949+ test ( 'should not detect vendor prefix for standard pseudo-elements' , ( ) => {
950+ let source = 'div::before { content: ""; }'
951+ let parser = new Parser ( source )
952+ let root = parser . parse ( )
953+
954+ let rule = root . first_child !
955+ let selector = rule . first_child !
956+ let typeSelector = selector . first_child !
957+ let pseudoElement = typeSelector . next_sibling !
958+ expect ( pseudoElement . name ) . toBe ( 'before' )
959+ expect ( pseudoElement . is_vendor_prefixed ) . toBe ( false )
960+ } )
961+
962+ test ( 'should detect vendor prefix with multiple vendor-prefixed pseudo-elements' , ( ) => {
963+ let source = 'div::-webkit-scrollbar { } div::-webkit-scrollbar-thumb { } div::after { }'
964+ let parser = new Parser ( source )
965+ let root = parser . parse ( )
966+
967+ let [ rule1 , rule2 , rule3 ] = root . children
968+
969+ let selector1 = rule1 . first_child !
970+ let typeSelector1 = selector1 . first_child !
971+ let pseudo1 = typeSelector1 . next_sibling !
972+ expect ( pseudo1 . name ) . toBe ( '-webkit-scrollbar' )
973+ expect ( pseudo1 . is_vendor_prefixed ) . toBe ( true )
974+
975+ let selector2 = rule2 . first_child !
976+ let typeSelector2 = selector2 . first_child !
977+ let pseudo2 = typeSelector2 . next_sibling !
978+ expect ( pseudo2 . name ) . toBe ( '-webkit-scrollbar-thumb' )
979+ expect ( pseudo2 . is_vendor_prefixed ) . toBe ( true )
980+
981+ let selector3 = rule3 . first_child !
982+ let typeSelector3 = selector3 . first_child !
983+ let pseudo3 = typeSelector3 . next_sibling !
984+ expect ( pseudo3 . name ) . toBe ( 'after' )
985+ expect ( pseudo3 . is_vendor_prefixed ) . toBe ( false )
986+ } )
987+
988+ test ( 'should detect vendor prefix in complex selector' , ( ) => {
989+ let source = 'input:-webkit-autofill:focus { color: black; }'
990+ let parser = new Parser ( source )
991+ let root = parser . parse ( )
992+
993+ let rule = root . first_child !
994+ let selector = rule . first_child !
995+ // Navigate through compound selector: input (type) -> -webkit-autofill (pseudo) -> :focus (pseudo)
996+ let typeSelector = selector . first_child !
997+ let webkitPseudo = typeSelector . next_sibling !
998+ expect ( webkitPseudo . name ) . toBe ( '-webkit-autofill' )
999+ expect ( webkitPseudo . is_vendor_prefixed ) . toBe ( true )
1000+
1001+ // Check the :focus pseudo-class is not vendor prefixed
1002+ let focusPseudo = webkitPseudo . next_sibling !
1003+ expect ( focusPseudo . name ) . toBe ( 'focus' )
1004+ expect ( focusPseudo . is_vendor_prefixed ) . toBe ( false )
1005+ } )
1006+ } )
1007+
8411008 describe ( 'complex real-world scenarios' , ( ) => {
8421009 test ( 'should parse complex nested structure' , ( ) => {
8431010 let source = `
0 commit comments