Repository: tailwindlabs/tailwindcss-jit (js)

Issue:
Keyframes malformed when using animation utilities with `@apply`
When you use `@apply` with any of the animation utilities, the keyframe children are missing their selectors:

```css
/* Input */
@tailwind utilities;

@layer utilities {
  .foo {
    @apply animate-spin;
  }
}

/* Output */
.foo {
  animation: spin 1s linear infinite
}

@keyframes spin {
   {
    transform: rotate(360deg)
  }
}
```

The actual `@keyframes` rule is generated later in the output than I would expect as well — I'd think it would come first.

Pull request:
Fix @apply of animation utilities
Fixes #94

Patch:
diff --git a/src/lib/expandApplyAtRules.js b/src/lib/expandApplyAtRules.js
index d0a46fe..d6b2eee 100644
--- a/src/lib/expandApplyAtRules.js
+++ b/src/lib/expandApplyAtRules.js
@@ -117,23 +117,27 @@ function expandApplyAtRules(context) {
 
           for (let [meta, node] of rules) {
             let root = postcss.root({ nodes: [node.clone()] })
+            let canRewriteSelector = node.type !== 'atrule' || (node.type === 'atrule' && node.name !== 'keyframes');
 
-            root.walkRules((rule) => {
-              rule.selector = replaceSelector(apply.parent.selector, rule.selector, applyCandidate)
-              rule.walkDecls((d) => {
-                d.important = important
+            if (canRewriteSelector) {
+              root.walkRules((rule) => {
+                rule.selector = replaceSelector(apply.parent.selector, rule.selector, applyCandidate)
+
+                rule.walkDecls((d) => {
+                  d.important = important
+                })
               })
-            })
+            }
 
             siblings.push([meta, root.nodes[0]])
           }
         }
 
         // Inject the rules, sorted, correctly
-        for (let [, sibling] of siblings.sort(([a], [z]) => bigSign(z.sort - a.sort))) {
-          // `apply.parent` is referring to the node at `.abc` in: .abc { @apply mt-2 }
-          apply.parent.after(sibling)
-        }
+        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)
 
         // If there are left-over declarations, just remove the @apply
         if (apply.parent.nodes.length > 1) {


Test patch:
diff --git a/tests/00-kitchen-sink.test.css b/tests/00-kitchen-sink.test.css
index cab8b2c..d43ebaa 100644
--- a/tests/00-kitchen-sink.test.css
+++ b/tests/00-kitchen-sink.test.css
@@ -243,8 +243,8 @@ div {
   --tw-numeric-fraction: var(--tw-empty, /*!*/ /*!*/);
   font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
     var(--tw-numeric-spacing) var(--tw-numeric-fraction);
-  --tw-numeric-spacing: tabular-nums;
   --tw-ordinal: ordinal;
+  --tw-numeric-spacing: tabular-nums;
 }
 .custom-component {
   background: #123456;
diff --git a/tests/10-apply.test.css b/tests/10-apply.test.css
index ddf73cf..cccd8b4 100644
--- a/tests/10-apply.test.css
+++ b/tests/10-apply.test.css
@@ -11,19 +11,19 @@
   border-radius: 0.375rem;
   --tw-bg-opacity: 1;
   background-color: rgba(59, 130, 246, var(--tw-bg-opacity));
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
   padding-left: 1rem;
   padding-right: 1rem;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
 }
 .class-order {
   padding: 2rem;
-  padding-top: 1.75rem;
-  padding-bottom: 1.75rem;
   padding-left: 0.75rem;
   padding-right: 0.75rem;
-  padding-right: 0.25rem;
+  padding-top: 1.75rem;
+  padding-bottom: 1.75rem;
   padding-top: 1rem;
+  padding-right: 0.25rem;
 }
 .with-additional-properties {
   font-weight: 500;
@@ -104,10 +104,10 @@
   border-radius: 0.375rem;
   --tw-bg-opacity: 1;
   background-color: rgba(59, 130, 246, var(--tw-bg-opacity));
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
   padding-left: 1rem;
   padding-right: 1rem;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
 }
 .multiple-variants:hover,
 .selectors-variants:hover {
@@ -149,8 +149,8 @@
   --tw-numeric-fraction: var(--tw-empty, /*!*/ /*!*/);
   font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure)
     var(--tw-numeric-spacing) var(--tw-numeric-fraction);
-  --tw-numeric-spacing: tabular-nums;
   --tw-ordinal: ordinal;
+  --tw-numeric-spacing: tabular-nums;
   --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
   box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
     var(--tw-shadow);
@@ -187,18 +187,18 @@
 }
 .btn {
   border-radius: 0.25rem;
-  padding-left: 1rem;
-  padding-right: 1rem;
   padding-top: 0.5rem;
   padding-bottom: 0.5rem;
+  padding-left: 1rem;
+  padding-right: 1rem;
   font-weight: 700;
 }
 .btn-blue {
   border-radius: 0.25rem;
-  padding-left: 1rem;
-  padding-right: 1rem;
   padding-top: 0.5rem;
   padding-bottom: 0.5rem;
+  padding-left: 1rem;
+  padding-right: 1rem;
   font-weight: 700;
   --tw-bg-opacity: 1;
   background-color: rgba(59, 130, 246, var(--tw-bg-opacity));
@@ -265,3 +265,19 @@
   color: green;
   font-weight: 700;
 }
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+.foo {
+  animation: spin 1s linear infinite;
+}
+@keyframes pulse {
+  50% {
+    opacity: 0.5;
+  }
+}
+.bar {
+  animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important;
+}
diff --git a/tests/10-apply.test.html b/tests/10-apply.test.html
index d355412..b63cc4b 100644
--- a/tests/10-apply.test.html
+++ b/tests/10-apply.test.html
@@ -32,5 +32,7 @@
     <div class="recursive-apply-c"></div>
     <div class="use-with-other-properties-base use-with-other-properties-component"></div>
     <div class="a b"></div>
+    <div class="foo"></div>
+    <div class="bar"></div>
   </body>
 </html>
diff --git a/tests/10-apply.test.js b/tests/10-apply.test.js
index 732ed03..8e0c3e0 100644
--- a/tests/10-apply.test.js
+++ b/tests/10-apply.test.js
@@ -105,6 +105,14 @@ test('@apply', () => {
     .custom-util {
       custom: stuff;
     }
+
+    .foo {
+      @apply animate-spin;
+    }
+
+    .bar {
+      @apply animate-pulse !important;
+    }
   }
 `