Repository: tailwindlabs/tailwindcss
Pull request #2832 (2020-11-19 23:58:49)

allow for recursively applying user defined classes

codecov-io: # [Codecov](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=h1) Report
> Merging [#2832](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=desc) (8f09dd2) into [master](https://codecov.io/gh/tailwindlabs/tailwindcss/commit/3ea5e18c243cf00bc4d317c7d2282b528b7cdb70?el=desc) (3ea5e18) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832/graphs/tree.svg?width=650&height=150&src=pr&token=bY4d7WZ54r)](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master    #2832   +/-   ##
=======================================
  Coverage   93.20%   93.20%           
=======================================
  Files         177      177           
  Lines        1795     1796    +1     
  Branches      317      317           
=======================================
+ Hits         1673     1674    +1     
  Misses        105      105           
  Partials       17       17           
```


| [Impacted Files](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/lib/substituteClassApplyAtRules.js](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832/diff?src=pr&el=tree#diff-c3JjL2xpYi9zdWJzdGl0dXRlQ2xhc3NBcHBseUF0UnVsZXMuanM=) | `94.52% <100.00%> (+0.03%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=footer). Last update [3ea5e18...8f09dd2](https://codecov.io/gh/tailwindlabs/tailwindcss/pull/2832?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/lib/substituteClassApplyAtRules.js b/src/lib/substituteClassApplyAtRules.js
index 95e4bce3..7c7c89ad 100644
--- a/src/lib/substituteClassApplyAtRules.js
+++ b/src/lib/substituteClassApplyAtRules.js
@@ -96,7 +96,7 @@ function buildUtilityMap(css, lookupTree) {
   let index = 0
   const utilityMap = {}
 
-  function handle(rule) {
+  function handle(getRule, rule) {
     const utilityNames = extractUtilityNames(rule.selector)
 
     utilityNames.forEach((utilityName, i) => {
@@ -108,16 +108,29 @@ function buildUtilityMap(css, lookupTree) {
         index,
         utilityName,
         classPosition: i,
-        get rule() {
-          return cloneRuleWithParent(rule)
-        },
+        ...getRule(rule),
       })
       index++
     })
   }
 
-  lookupTree.walkRules(handle)
-  css.walkRules(handle)
+  // Lookup tree is the big lookup tree, making the rule lazy allows us to save
+  // some memory because we don't need everything.
+  lookupTree.walkRules(
+    handle.bind(null, (rule) => ({
+      get rule() {
+        return cloneRuleWithParent(rule)
+      },
+    }))
+  )
+
+  // This is the end user's css. This might contain rules that we want to
+  // apply. We want immediate copies of everything in case that we have user
+  // defined classes that are recursively applied. Down below we are modifying
+  // the rules directly. We could do a better solution where we keep track of a
+  // dependency tree, but that is a bit more complex. Might revisit later,
+  // we'll see how this turns out!
+  css.walkRules(handle.bind(null, (rule) => ({ rule: cloneRuleWithParent(rule) })))
 
   return utilityMap
 }


Test patch:
diff --git a/__tests__/applyAtRule.test.js b/__tests__/applyAtRule.test.js
index 36a86293..df8da5bf 100644
--- a/__tests__/applyAtRule.test.js
+++ b/__tests__/applyAtRule.test.js
@@ -593,7 +593,7 @@ test('you can apply classes recursively', () => {
   })
 })
 
-test.skip('you can apply complex classes recursively', () => {
+test('you can apply complex classes recursively', () => {
   const input = `
     .button {
       @apply rounded-xl px-6 py-2 hover:text-white focus:border-opacity-100;
@@ -603,10 +603,12 @@ test.skip('you can apply complex classes recursively', () => {
       @apply button bg-yellow-600 text-gray-200;
     }
   `
+
   const expected = `
     .button:focus {
       --tw-border-opacity: 1;
     }
+
     .button {
       border-radius: 0.75rem;
       padding-top: 0.5rem;
@@ -614,13 +616,23 @@ test.skip('you can apply complex classes recursively', () => {
       padding-left: 1.5rem;
       padding-right: 1.5rem;
     }
+
     .button:hover {
       --tw-text-opacity: 1;
       color: rgba(255, 255, 255, var(--tw-text-opacity));
     }
+
+    .button-yellow {
+      --tw-bg-opacity: 1;
+      background-color: rgba(217, 119, 6, var(--tw-bg-opacity));
+      --tw-text-opacity: 1;
+      color: rgba(229, 231, 235, var(--tw-text-opacity));
+    }
+
     .button-yellow:focus {
       --tw-border-opacity: 1;
     }
+
     .button-yellow {
       border-radius: 0.75rem;
       padding-top: 0.5rem;
@@ -628,16 +640,11 @@ test.skip('you can apply complex classes recursively', () => {
       padding-left: 1.5rem;
       padding-right: 1.5rem;
     }
+
     .button-yellow:hover {
       --tw-text-opacity: 1;
       color: rgba(255, 255, 255, var(--tw-text-opacity));
     }
-    .button-yellow {
-      --tw-bg-opacity: 1;
-      background-color: rgba(217, 119, 6, var(--tw-bg-opacity));
-      --tw-text-opacity: 1;
-      color: rgba(229, 231, 235, var(--tw-text-opacity));
-    }
   `
 
   expect.assertions(2)