Repository: postcss/autoprefixer
Pull request #1253 (2019-10-08 11:35:34)

Bugfix/separate selectors
Not sure about implementation at all but that's how I see it from my understanding of how the PostCSS and Autoprefixer works :relaxed:

ai: Wow, great. This fix is great (and looks like correct) but should have a double check (and I am too drunk right now). I will try to check it this week.
fanich37: > Wow, great. This fix is great (and looks like correct) but should have a double check (and I am too drunk right now). I will try to check it this week.

Thank for your fast reply! It would be awesome if you found some time to test it more precisely. But it's definitely not the fix to rush with :+1: 

ai: Great! Now it became much simpler. 

Patch:
diff --git a/lib/selector.js b/lib/selector.js
index 123f38f..2b8f68c 100644
--- a/lib/selector.js
+++ b/lib/selector.js
@@ -1,3 +1,5 @@
+let { list } = require('postcss')
+
 let OldSelector = require('./old-selector')
 let Prefixer = require('./prefixer')
 let Browsers = require('./browsers')
@@ -53,15 +55,30 @@ class Selector extends Prefixer {
      */
   prefixeds (rule) {
     if (rule._autoprefixerPrefixeds) {
-      return rule._autoprefixerPrefixeds
+      if (rule._autoprefixerPrefixeds[this.name]) {
+        return rule._autoprefixerPrefixeds
+      }
+    } else {
+      rule._autoprefixerPrefixeds = {}
     }
 
     let prefixeds = {}
-    for (let prefix of this.possible()) {
-      prefixeds[prefix] = this.replace(rule.selector, prefix)
+    if (rule.selector.includes(',')) {
+      let ruleParts = list.comma(rule.selector)
+      let toProcess = ruleParts.filter(el => el.includes(this.name))
+
+      for (let prefix of this.possible()) {
+        prefixeds[prefix] = toProcess
+          .map(el => this.replace(el, prefix))
+          .join(', ')
+      }
+    } else {
+      for (let prefix of this.possible()) {
+        prefixeds[prefix] = this.replace(rule.selector, prefix)
+      }
     }
 
-    rule._autoprefixerPrefixeds = prefixeds
+    rule._autoprefixerPrefixeds[this.name] = prefixeds
     return rule._autoprefixerPrefixeds
   }
 
@@ -79,8 +96,8 @@ class Selector extends Prefixer {
       }
 
       let some = false
-      for (let key in prefixeds) {
-        let prefixed = prefixeds[key]
+      for (let key in prefixeds[this.name]) {
+        let prefixed = prefixeds[this.name][key]
         if (before.selector === prefixed) {
           if (prefix === key) {
             return true
@@ -117,7 +134,7 @@ class Selector extends Prefixer {
       return
     }
 
-    let cloned = this.clone(rule, { selector: prefixeds[prefix] })
+    let cloned = this.clone(rule, { selector: prefixeds[this.name][prefix] })
     rule.parent.insertBefore(rule, cloned)
   }
 


Test patch:
diff --git a/test/autoprefixer.test.js b/test/autoprefixer.test.js
index f05096a..ee677ea 100644
--- a/test/autoprefixer.test.js
+++ b/test/autoprefixer.test.js
@@ -44,6 +44,10 @@ let uncascader = autoprefixer({
 let gradienter = autoprefixer({
   overrideBrowserslist: ['Chrome 25', 'Opera 12', 'Android 2.3']
 })
+let grouping = autoprefixer({
+  overrideBrowserslist: ['Chrome 25', 'Firefox > 17', 'IE 10', 'Edge 12'],
+  grid: 'autoplace'
+})
 let ffgradienter = autoprefixer({
   overrideBrowserslist: ['Chrome 25', 'Opera 12', 'Firefox 6']
 })
@@ -90,6 +94,8 @@ function prefixer (name) {
     return gradienter
   } else if (name === 'gradient-fix') {
     return ffgradienter
+  } else if (name === 'grouping-rule') {
+    return grouping
   } else if (name === 'flexbox' || name === 'flex-rewrite') {
     return flexboxer
   } else if (name === 'double') {
@@ -244,6 +250,7 @@ it('saves declaration style', () => check('style'))
 it('uses ignore next control comments', () => check('ignore-next'))
 it('uses block control comments', () => check('disabled'))
 it('has actual example in docs', () => check('example'))
+it('process grouping rules correctly', () => check('grouping-rule'))
 
 it('uses control comments to whole scope', () => {
   let input = read('scope')
diff --git a/test/cases/grouping-rule.css b/test/cases/grouping-rule.css
new file mode 100644
index 0000000..0a59e47
--- /dev/null
+++ b/test/cases/grouping-rule.css
@@ -0,0 +1,23 @@
+.grid {
+  display: grid;
+}
+
+.a,
+.b,
+.c::selection,
+.d:read-only,
+.e::placeholder {
+  color: yellow;
+}
+
+::selection {
+  color: red;
+}
+
+:read-only {
+  color: black;
+}
+
+.f:read-write, .g:read-write {
+  background: #fff;
+}
diff --git a/test/cases/grouping-rule.out.css b/test/cases/grouping-rule.out.css
new file mode 100644
index 0000000..829872b
--- /dev/null
+++ b/test/cases/grouping-rule.out.css
@@ -0,0 +1,64 @@
+.grid {
+  display: -ms-grid;
+  display: grid;
+}
+
+.c::-moz-selection {
+  color: yellow;
+}
+
+.e::-webkit-input-placeholder {
+  color: yellow;
+}
+
+.e:-moz-placeholder {
+  color: yellow;
+}
+
+.e::-moz-placeholder {
+  color: yellow;
+}
+
+.e:-ms-input-placeholder {
+  color: yellow;
+}
+
+.e::-ms-input-placeholder {
+  color: yellow;
+}
+
+.d:-moz-read-only {
+  color: yellow;
+}
+
+.a,
+.b,
+.c::selection,
+.d:read-only,
+.e::placeholder {
+  color: yellow;
+}
+
+::-moz-selection {
+  color: red;
+}
+
+::selection {
+  color: red;
+}
+
+:-moz-read-only {
+  color: black;
+}
+
+:read-only {
+  color: black;
+}
+
+.f:-moz-read-write, .g:-moz-read-write {
+  background: #fff;
+}
+
+.f:read-write, .g:read-write {
+  background: #fff;
+}
diff --git a/test/selector.test.js b/test/selector.test.js
index de590fd..afe4109 100644
--- a/test/selector.test.js
+++ b/test/selector.test.js
@@ -28,7 +28,7 @@ describe('regexp()', () => {
 })
 
 describe('check()', () => {
-  it('shecks rule selectors', () => {
+  it('checks rule selectors', () => {
     let css = parse('body .selection {}, ' +
             ':::selection {}, body ::selection {}')
     expect(selector.check(css.nodes[0])).toBeFalsy()
@@ -38,13 +38,36 @@ describe('check()', () => {
 })
 
 describe('prefixeds()', () => {
-  it('returns all avaiable prefixed selectors', () => {
+  it('grouping rule gets correct _autoprefixerPrefixeds property', () => {
+    let css = parse('.c::selection, .d:read-only {}')
+    let rSel = new Selector(':read-only', ['-moz-'])
+    selector.prefixeds(css.first)
+    rSel.prefixeds(css.first)
+    expect(css.first._autoprefixerPrefixeds).toEqual({
+      '::selection': {
+        '-webkit-': '.c::-webkit-selection',
+        '-moz-': '.c::-moz-selection',
+        '-ms-': '.c::-ms-selection',
+        '-o-': '.c::-o-selection'
+      },
+      ':read-only': {
+        '-webkit-': '.d:-webkit-read-only',
+        '-moz-': '.d:-moz-read-only',
+        '-ms-': '.d:-ms-read-only',
+        '-o-': '.d:-o-read-only'
+      }
+    })
+  })
+
+  it('returns all available prefixed selectors', () => {
     let css = parse('::selection {}')
     expect(selector.prefixeds(css.first)).toEqual({
-      '-webkit-': '::-webkit-selection',
-      '-moz-': '::-moz-selection',
-      '-ms-': '::-ms-selection',
-      '-o-': '::-o-selection'
+      '::selection': {
+        '-webkit-': '::-webkit-selection',
+        '-moz-': '::-moz-selection',
+        '-ms-': '::-ms-selection',
+        '-o-': '::-o-selection'
+      }
     })
   })
 })