Repository: postcss/autoprefixer (js)

Issue:
[css-grid] media rules are not inserted for grid-template[-areas] inside media queries
**Description**
There is a new bug where there is no `lastRule` set when `grid-template-areas` are declared within a media query. The `insertAreas()` utility function attempts to media rules for each area, but fails because it has no designated rule to append them to.
*NOTE:* This was working correctly in version 9.1.5.

**Expected Result**
For `grid-template-areas` declared within media queries, new media queries are inserted for each `grid-area` defined in the template.

**Proposed Solution**
I believe the bug was introduced in version 9.2:
https://github.com/postcss/autoprefixer/blob/2072e73d77fd1abc67e49f74950e3df2d6599026/lib/hacks/grid-utils.js#L606-L608
The issue goes away if the `if` condition is removed and you, instead, just have:
```
        rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule
```

**CSS Example to Reproduce**
Input:
```
.example {
  display: grid;
}

@media (max-width: 767.98px) {
  .example {
    grid-template-areas:
      'first first'
      'second third';
  }
}

.first {
  grid-area: first;
}
  
.second {
  grid-area: second;
}

.third {
  grid-area: third;
}
```

Current output:
```
.example {
  display: -ms-grid;
  display: grid;
}

@media (max-width: 767.98px) {
  .example {
        grid-template-areas:
      'first first'
      'second third';
  }
}

.first {
  grid-area: first;
}
  
.second {
  grid-area: second;
}

.third {
  grid-area: third;
}
```

Expected output:
```
.example[_ngcontent-c23] {
  display: -ms-grid;
  display: grid;
}

@media (max-width: 767.98px) {
  .example[_ngcontent-c23] {
        grid-template-areas: 'first first' 'second third';
  }
}

.first[_ngcontent-c23] {
  grid-area: first;
}

.second[_ngcontent-c23] {
  grid-area: second;
}

.third[_ngcontent-c23] {
  grid-area: third;
}

@media (max-width: 767.98px) {
  .first[_ngcontent-c23] {
    -ms-grid-row: 1;
    -ms-grid-column: 1;
    -ms-grid-column-span: 2;
  }
  .second[_ngcontent-c23] {
    -ms-grid-row: 2;
    -ms-grid-column: 1;
  }
  .third[_ngcontent-c23] {
    -ms-grid-row: 2;
    -ms-grid-column: 2;
  }
}
```


*NOTE:* While I was investigating this, I also noticed these lines:
https://github.com/postcss/autoprefixer/blob/2072e73d77fd1abc67e49f74950e3df2d6599026/lib/hacks/grid-utils.js#L534-L537
I have not run into any issues from this, but it looks like it was meant to be something like:
```
        if (rulesToInsert[lastArea].lastRule) {
          rulesToInsert[lastArea].lastRule.after(cloned)
        } else {
          rulesToInsert[lastArea].lastRule = cloned
        }
```

Pull request:
[css-grid] Fix bug when grid-area rules are not inserted
Fixes #1191 

/cc @Dan503 for PR review 🙂

@cekejiuba We would be happy if you could test the fix in your spare time. Here are the things you need to do:

1. Clone my branch: `git clone -b fix/grid-areas-bug https://github.com/bogdan0083/autoprefixer`
2. run `yarn` inside cloned repository
3. run `npx gulp play`, add the code to `./playground/input.css` and save the file
4. the output will be in `./playground/output.css`

Patch:
diff --git a/lib/hacks/grid-utils.js b/lib/hacks/grid-utils.js
index 6799d98..9681bc9 100644
--- a/lib/hacks/grid-utils.js
+++ b/lib/hacks/grid-utils.js
@@ -503,6 +503,8 @@ function insertAreas (css, isDisabled) {
       rulesToInsert[lastArea] = {}
     }
 
+    let lastRuleIsSet = false
+
     // walk through every grid-template rule data
     for (let rule of data.rules) {
       let area = rule.areas[value]
@@ -536,6 +538,7 @@ function insertAreas (css, isDisabled) {
           ))
 
         rulesToInsert[lastArea].lastRule = gridAreaRule
+        lastRuleIsSet = true
       } else if (rule.hasDuplicates && !rule.params && !selectorIsComplex) {
         // grid-template has duplicates and not inside media rule
         let cloned = gridAreaRule.clone()
@@ -558,6 +561,7 @@ function insertAreas (css, isDisabled) {
           rulesToInsert[lastArea].lastRule.after(cloned)
         }
         rulesToInsert[lastArea].lastRule = cloned
+        lastRuleIsSet = true
       } else if (
         rule.hasDuplicates &&
         !rule.params &&
@@ -565,7 +569,7 @@ function insertAreas (css, isDisabled) {
         gridAreaRule.selector.includes(rule.selectors[0])
       ) {
         // grid-template has duplicates and not inside media rule
-        // see the bottom of grid-template-areas test case for example
+        // and the selector is complex
         gridAreaRule.walkDecls(/-ms-grid-(row|column)/, d => d.remove())
         getMSDecls(area, area.row.updateSpan, area.column.updateSpan).reverse()
           .forEach(i => gridAreaRule.prepend(
@@ -609,7 +613,9 @@ function insertAreas (css, isDisabled) {
           rulesToInsert[lastArea][rule.params].push(cloned)
         }
 
-        if (gridAreaMedia) {
+        // set new rule as last rule ONLY if we didn't set lastRule for
+        // this grid-area before
+        if (!lastRuleIsSet) {
           rulesToInsert[lastArea].lastRule = gridAreaMedia || gridAreaRule
         }
       }


Test patch:
diff --git a/test/cases/grid-template-areas.css b/test/cases/grid-template-areas.css
index 02454fb..68aa633 100644
--- a/test/cases/grid-template-areas.css
+++ b/test/cases/grid-template-areas.css
@@ -215,3 +215,27 @@
   /* autoprefixer: ignore next */
   grid-area: child;
 }
+
+.grid-example {
+  display: grid;
+}
+
+@media (max-width: 767.98px) {
+  .grid-example {
+    grid-template-areas:
+      'first first'
+      'second third';
+  }
+}
+
+.grid-area-first {
+  grid-area: first;
+}
+  
+.grid-area-second {
+  grid-area: second;
+}
+
+.grid-area-third {
+  grid-area: third;
+}
diff --git a/test/cases/grid-template-areas.out.css b/test/cases/grid-template-areas.out.css
index 34b54a5..ba28ced 100644
--- a/test/cases/grid-template-areas.out.css
+++ b/test/cases/grid-template-areas.out.css
@@ -351,3 +351,44 @@
   /* autoprefixer: ignore next */
   grid-area: child;
 }
+
+.grid-example {
+  display: -ms-grid;
+  display: grid;
+}
+
+@media (max-width: 767.98px) {
+  .grid-example {
+    grid-template-areas:
+      'first first'
+      'second third';
+  }
+}
+
+.grid-area-first {
+  grid-area: first;
+}
+  
+.grid-area-second {
+  grid-area: second;
+}
+
+.grid-area-third {
+  grid-area: third;
+}
+
+@media (max-width: 767.98px) {
+  .grid-area-first {
+    -ms-grid-row: 1;
+    -ms-grid-column: 1;
+    -ms-grid-column-span: 2;
+  }
+  .grid-area-second {
+    -ms-grid-row: 2;
+    -ms-grid-column: 1;
+  }
+  .grid-area-third {
+    -ms-grid-row: 2;
+    -ms-grid-column: 2;
+  }
+}