Repository: tailwindlabs/tailwindcss (ts)

Issue:
PurgeCSS remove first components class when using whitelist comment
Hi tailwindlabs team,

I noticed the following point with the building PurgeCSS.

If I import a CSS file before `@tailwind components;` and use PurgeCSS whitelist comment: `.container` classes are remove but not responsive container classes. 

My Sass file
```
@tailwind base;

/* purgecss start ignore */
@import "button";
/* purgecss end ignore */

@tailwind components;

@tailwind utilities;
```

I make different test using exclamation mark in comment, ... same result

I use last tailwindcss 1.7.5, sass, csso.

My `tailwind.config.js`
```
module.exports = {
  purge: [
    'path/to/templates',
  ],
  future: {
    removeDeprecatedGapUtilities: true,
  },
  experimental: {
    applyComplexClasses: true,
    uniformColorPalette: true,
    extendedSpacingScale: true,
    defaultLineHeights: true,
    extendedFontSizeScale: true,
  },
}
```

Pull request description:
Fix issue where inserting extra PurgeCSS control comments could break integrated PurgeCSS support
Fixes #2258.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d88321de..19e4e1e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
-- Nothing new yet!
+- Fix [issue](https://github.com/tailwindlabs/tailwindcss/issues/2258) where inserting extra PurgeCSS control comments could break integrated PurgeCSS support
 
 ## [1.8.3] - 2020-09-05
 
diff --git a/src/lib/purgeUnusedStyles.js b/src/lib/purgeUnusedStyles.js
index 4a38fc33..5b0404ae 100644
--- a/src/lib/purgeUnusedStyles.js
+++ b/src/lib/purgeUnusedStyles.js
@@ -77,10 +77,18 @@ export default function purgeUnusedUtilities(config, configChanged) {
           ? ['utilities']
           : _.get(config, 'purge.layers', ['base', 'components', 'utilities'])
 
-      css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
-      css.append(postcss.comment({ text: 'purgecss end ignore' }))
-
       css.walkComments(comment => {
+        switch (comment.text.trim()) {
+          case `purgecss start ignore`:
+            comment.before(postcss.comment({ text: 'purgecss end ignore' }))
+            break
+          case `purgecss end ignore`:
+            comment.before(postcss.comment({ text: 'purgecss end ignore' }))
+            comment.text = 'purgecss start ignore'
+            break
+          default:
+            break
+        }
         layers.forEach(layer => {
           switch (comment.text.trim()) {
             case `tailwind start ${layer}`:
@@ -94,6 +102,9 @@ export default function purgeUnusedUtilities(config, configChanged) {
           }
         })
       })
+
+      css.prepend(postcss.comment({ text: 'purgecss start ignore' }))
+      css.append(postcss.comment({ text: 'purgecss end ignore' }))
     },
     removeTailwindMarkers,
     purgecss({


Test patch:
diff --git a/__tests__/purgeUnusedStyles.test.js b/__tests__/purgeUnusedStyles.test.js
index 7929d973..dd6f7f3c 100644
--- a/__tests__/purgeUnusedStyles.test.js
+++ b/__tests__/purgeUnusedStyles.test.js
@@ -195,19 +195,19 @@ test('custom css in a layer is purged by default when using layers mode', () =>
       ])
         .process(
           `
-        @tailwind base;
+          @tailwind base;
 
-        @tailwind components;
+          @tailwind components;
 
-        @layer components {
-          .example {
-            @apply font-bold;
-            color: theme('colors.red.500');
+          @layer components {
+            .example {
+              @apply font-bold;
+              color: theme('colors.red.500');
+            }
           }
-        }
 
-        @tailwind utilities;
-      `,
+          @tailwind utilities;
+        `,
           { from: null }
         )
         .then(result => {
@@ -411,6 +411,43 @@ test('does not purge components when mode is conservative', () => {
   )
 })
 
+test('extra purgecss control comments can be added manually', () => {
+  return inProduction(
+    suppressConsoleLogs(() => {
+      const input = `
+        @tailwind base;
+
+        /* purgecss start ignore */
+        .btn {
+          background: red;
+        }
+        /* purgecss end ignore */
+
+        @tailwind components;
+        @tailwind utilities;
+      `
+
+      return postcss([
+        tailwind({
+          ...config,
+          purge: {
+            layers: ['utilities'],
+            content: [path.resolve(`${__dirname}/fixtures/**/*.html`)],
+          },
+        }),
+      ])
+        .process(input, { from: null })
+        .then(result => {
+          const rules = extractRules(result.root)
+
+          expect(rules).toContain('.btn')
+          expect(rules).toContain('.container')
+          assertPurged(result)
+        })
+    })
+  )
+})
+
 test(
   'does not purge except in production',
   suppressConsoleLogs(() => {