Repository: tailwindlabs/tailwindcss-jit (js)

Issue:
Wrong output order when using multiple @apply rules
### What version of @tailwindcss/jit are you using?

v0.1.7

### What version of Node.js are you using?

v.15.something

### What browser are you using?

Chrome

### What operating system are you using?

macOs

I noticed that the CSS output is in the wrong order if you use multiple `@apply` rules like this:

```
h1 {
  @apply text-2xl;
  @apply sm:text-3xl;
}
```

With this example the ` sm:text-3xl` rule is overwritten by `text-2xl`.

It does work as expected when I combine the rules in one `@apply` statement like this:

```
h1 {
  @apply text-2xl sm:text-3xl;
}
```

I wasn't sure if this is related to existing issues about @apply. If so, sorry for the duplicate issue 🙃.

Pull request description:
Fix multiple @apply involving responsive rules
Builds on #150 (so lets get that merged first and then I'll rebase this)

Closes #148

Patch:
diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js
index d6b2eee..b969074 100644
--- a/src/lib/expandApplyAtRules.js
+++ b/src/lib/expandApplyAtRules.js
@@ -102,8 +102,15 @@ function expandApplyAtRules(context) {
           .join(', ')
       }
 
+      /** @type {Map<import('postcss').Node, [string, boolean, import('postcss').Node[]][]>} */
+      let perParentApplies = new Map()
+
+      // Collect all apply candidates and their rules
       for (let apply of applies) {
-        let siblings = []
+        let candidates = perParentApplies.get(apply.parent) || []
+
+        perParentApplies.set(apply.parent, candidates)
+
         let [applyCandidates, important] = extractApplyCandidates(apply.params)
 
         for (let applyCandidate of applyCandidates) {
@@ -115,13 +122,22 @@ function expandApplyAtRules(context) {
 
           let rules = applyClassCache.get(applyCandidate)
 
+          candidates.push([applyCandidate, important, rules])
+        }
+      }
+
+      for (const [parent, candidates] of perParentApplies) {
+        let siblings = []
+
+        for (let [applyCandidate, important, rules] of candidates) {
           for (let [meta, node] of rules) {
             let root = postcss.root({ nodes: [node.clone()] })
-            let canRewriteSelector = node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes');
+            let canRewriteSelector =
+              node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes')
 
             if (canRewriteSelector) {
               root.walkRules((rule) => {
-                rule.selector = replaceSelector(apply.parent.selector, rule.selector, applyCandidate)
+                rule.selector = replaceSelector(parent.selector, rule.selector, applyCandidate)
 
                 rule.walkDecls((d) => {
                   d.important = important
@@ -134,11 +150,13 @@ function expandApplyAtRules(context) {
         }
 
         // Inject the rules, sorted, correctly
-        const nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map(s => s[1])
+        const nodes = siblings.sort(([a], [z]) => bigSign(a.sort - z.sort)).map((s) => s[1])
 
-        // `apply.parent` is referring to the node at `.abc` in: .abc { @apply mt-2 }
-        apply.parent.after(nodes)
+        // `parent` refers to the node at `.abc` in: .abc { @apply mt-2 }
+        parent.after(nodes)
+      }
 
+      for (let apply of applies) {
         // If there are left-over declarations, just remove the @apply
         if (apply.parent.nodes.length > 1) {
           apply.remove()


Test patch:
diff --git a/tests/10-apply.test.css b/tests/10-apply.test.css
index cccd8b4..dd7ee96 100644
--- a/tests/10-apply.test.css
+++ b/tests/10-apply.test.css
@@ -265,6 +265,38 @@
   color: green;
   font-weight: 700;
 }
+h1 {
+  font-size: 1.5rem;
+  line-height: 2rem;
+}
+@media (min-width: 640px) {
+  h1 {
+    font-size: 1.875rem;
+    line-height: 2.25rem;
+  }
+}
+@media (min-width: 1024px) {
+  h1 {
+    font-size: 1.5rem;
+    line-height: 2rem;
+  }
+}
+h2 {
+  font-size: 1.5rem;
+  line-height: 2rem;
+}
+@media (min-width: 640px) {
+  h2 {
+    font-size: 1.5rem;
+    line-height: 2rem;
+  }
+}
+@media (min-width: 1024px) {
+  h2 {
+    font-size: 1.5rem;
+    line-height: 2rem;
+  }
+}
 @keyframes spin {
   to {
     transform: rotate(360deg);
diff --git a/tests/10-apply.test.js b/tests/10-apply.test.js
index 8e0c3e0..a9fa533 100644
--- a/tests/10-apply.test.js
+++ b/tests/10-apply.test.js
@@ -99,6 +99,15 @@ test('@apply', () => {
     .use-with-other-properties-component {
       @apply use-with-other-properties-base;
     }
+
+    h1 {
+      @apply text-2xl lg:text-2xl sm:text-3xl;
+    }
+    h2 {
+      @apply text-2xl;
+      @apply lg:text-2xl;
+      @apply sm:text-2xl;
+    }
   }
 
   @layer utilities {