Repository: tailwindlabs/tailwindcss (TypeScript)

Issue:
Jit crash with comment in `@layer utilities`
**What version of Tailwind CSS are you using?**

v2.2.17

**What build tool (or framework if it abstracts the build tool) are you using?**

Vite 2.6.7

**What version of Node.js are you using?**

For example: v16.11.0

**What browser are you using?**

N/A

**What operating system are you using?**

macOS

**Reproduction URL**

N/A

**Describe your issue**

With the following `tailwind.css` file, Jit mode crashes because of the "hello" comment:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer utilities {
  /* hello */
  .test {
    color: red;
  }
}
```

It is important to reproduce the crash that the comment be inside the `@layer utilities` block, just before a selector.

Stack trace:

```
rule.walkDecls is not a function
      at /Users/targos/test/node_modules/tailwindcss/lib/jit/lib/generateRules.js:338:14
      at Array.map (<anonymous>)
      at generateRules (/Users/targos/test/node_modules/tailwindcss/lib/jit/lib/generateRules.js:330:27)
      at /Users/targos/test/node_modules/tailwindcss/lib/jit/lib/expandTailwindAtRules.js:186:50
      at /Users/targos/test/node_modules/tailwindcss/lib/jit/processTailwindFeatures.js:60:49
      at /Users/targos/test/node_modules/tailwindcss/lib/jit/index.js:25:56
      at LazyResult.runOnRoot (/Users/targos/test/node_modules/postcss/lib/lazy-result.js:339:16)
      at LazyResult.runAsync (/Users/targos/test/node_modules/postcss/lib/lazy-result.js:391:26)
      at LazyResult.async (/Users/targos/test/node_modules/postcss/lib/lazy-result.js:221:30)
      at LazyResult.then (/Users/targos/test/node_modules/postcss/lib/lazy-result.js:206:17) (x3)
```


Hints:
Hey! Thank you for your bug report!
Much appreciated! 🙏

I can't reproduce it with this setup, but can you share your full tailwind.config.js?
I can reproduce it when I have `important: true`, but I want to verify that you have this as well.
Oh, sorry I forgot to put the config I used. Indeed, we have `important: true`:

It can be reproduced with:
```js
module.exports = {
  mode: 'jit',
  important: true,
  purge: ['./src/**/*.{js,ts,jsx,tsx}', './index.html'],
  darkMode: false,
};

```

Patch:
diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js
--- a/src/lib/generateRules.js
+++ b/src/lib/generateRules.js
@@ -448,28 +448,42 @@ function generateRules(candidates, context) {
     allRules.push(matches)
   }
 
-  return allRules.flat(1).map(([{ sort, layer, options }, rule]) => {
-    if (options.respectImportant) {
-      if (context.tailwindConfig.important === true) {
+  // Strategy based on `tailwindConfig.important`
+  let strategy = ((important) => {
+    if (important === true) {
+      return (rule) => {
         rule.walkDecls((d) => {
           if (d.parent.type === 'rule' && !inKeyframes(d.parent)) {
             d.important = true
           }
         })
-      } else if (typeof context.tailwindConfig.important === 'string') {
+      }
+    }
+
+    if (typeof important === 'string') {
+      return (rule) => {
+        rule.selectors = rule.selectors.map((selector) => {
+          return `${important} ${selector}`
+        })
+      }
+    }
+  })(context.tailwindConfig.important)
+
+  return allRules.flat(1).map(([{ sort, layer, options }, rule]) => {
+    if (options.respectImportant) {
+      if (strategy) {
         let container = postcss.root({ nodes: [rule.clone()] })
         container.walkRules((r) => {
           if (inKeyframes(r)) {
             return
           }
 
-          r.selectors = r.selectors.map((selector) => {
-            return `${context.tailwindConfig.important} ${selector}`
-          })
+          strategy(r)
         })
         rule = container.nodes[0]
       }
     }
+
     return [sort | context.layerOrder[layer], rule]
   })
 }


Test patch:
diff --git a/tests/layer-at-rules.test.js b/tests/layer-at-rules.test.js
--- a/tests/layer-at-rules.test.js
+++ b/tests/layer-at-rules.test.js
@@ -50,6 +50,125 @@ test('custom user-land utilities', () => {
   })
 })
 
+test('comments can be used inside layers without crashing', () => {
+  let config = {
+    content: [
+      {
+        raw: html`<div class="important-utility important-component"></div>`,
+      },
+    ],
+    corePlugins: { preflight: false },
+    theme: {},
+    plugins: [],
+  }
+
+  let input = css`
+    @tailwind base;
+    @tailwind components;
+    @tailwind utilities;
+
+    @layer base {
+      /* Important base */
+      div {
+        background-color: #bada55;
+      }
+    }
+
+    @layer utilities {
+      /* Important utility */
+      .important-utility {
+        text-align: banana;
+      }
+    }
+
+    @layer components {
+      /* Important component */
+      .important-component {
+        text-align: banana;
+      }
+    }
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.css).toMatchFormattedCss(css`
+      /* Important base */
+      div {
+        background-color: #bada55;
+      }
+
+      /* Important component */
+      .important-component {
+        text-align: banana;
+      }
+
+      /* Important utility */
+      .important-utility {
+        text-align: banana;
+      }
+    `)
+  })
+})
+
+test('comments can be used inside layers (with important) without crashing', () => {
+  let config = {
+    important: true,
+    content: [
+      {
+        raw: html`<div class="important-utility important-component"></div>`,
+      },
+    ],
+    corePlugins: { preflight: false },
+    theme: {},
+    plugins: [],
+  }
+
+  let input = css`
+    @tailwind base;
+    @tailwind components;
+    @tailwind utilities;
+
+    @layer base {
+      /* Important base */
+      div {
+        background-color: #bada55;
+      }
+    }
+
+    @layer utilities {
+      /* Important utility */
+      .important-utility {
+        text-align: banana;
+      }
+    }
+
+    @layer components {
+      /* Important component */
+      .important-component {
+        text-align: banana;
+      }
+    }
+  `
+
+  return run(input, config).then((result) => {
+    expect(result.css).toMatchFormattedCss(css`
+      /* Important base */
+      div {
+        background-color: #bada55;
+      }
+
+      /* Important component */
+      .important-component {
+        text-align: banana;
+      }
+
+      /* Important utility */
+      .important-utility {
+        text-align: banana !important;
+      }
+    `)
+  })
+})
+
 test('layers are grouped and inserted at the matching @tailwind rule', () => {
   let config = {
     content: [