Repository: maizzle/framework (js)

Issue:
Support tailwindlabs/tailwindcss-jit
Is it planned to generate minimal css with the new tailwindlabs/tailwindcss-jit instead of purging from the traditional huge set of Tailwind classes?

In my use case the files generated by Maizzle become templates consumed by another templating system (.eex / Elixir). As those .eex templates should be up to date during development on each edit, I do a `maizzle build production` of all templates on each save. For this use case it would be relevant to improve the build times with tailwindlabs/tailwindcss-jit.

I would be happy to contribute with a PR if you have a hint where to start.

Pull request description:
Refactor Tailwind CSS purging
Fixes #422 

This PR changes the way Tailwind is compiled internally.

- upgrade Tailwind to latest version
- remove `@fullhuman/postcss-purgecss`
- use Tailwind's own `purge` option instead of a custom strategy

To use JIT, you need to also specify the `purge` option in your `tailwind.config.js`, **in simplified array syntax**:

```js
module.exports = {
  mode: 'jit',
  purge: [
    'src/**/*.*',
  ],
  // ...
}
```

Patch:
diff --git a/package-lock.json b/package-lock.json
index 58e6d28..5a9b2ce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,6 @@
       "version": "3.3.1",
       "license": "MIT",
       "dependencies": {
-        "@fullhuman/postcss-purgecss": "^4.0.0",
         "browser-sync": "^2.26.13",
         "color-shorthand-hex-to-six-digit": "^3.0.2",
         "email-comb": "^5.0.0",
@@ -42,7 +41,7 @@
         "prevent-widows": "^1.0.2",
         "query-string": "^6.13.7",
         "string-strip-html": "^8.2.0",
-        "tailwindcss": "^2.0.3",
+        "tailwindcss": "^2.1.1",
         "tailwindcss-box-shadow": "^1.0.0"
       },
       "devDependencies": {
@@ -515,17 +514,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/@fullhuman/postcss-purgecss": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.2.tgz",
-      "integrity": "sha512-Qm8M89v4HVUTBQwuWREtzQojSqAbdp98kALMEw2NTrtrgHVqbLQrdiTBr0DAiNmG3hcqXSEexF/78uNUthMM9w==",
-      "dependencies": {
-        "purgecss": "^4.0.2"
-      },
-      "peerDependencies": {
-        "postcss": "^8.0.0"
-      }
-    },
     "node_modules/@istanbuljs/load-nyc-config": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -583,7 +571,6 @@
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
       "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "2.0.4",
         "run-parallel": "^1.1.9"
@@ -596,7 +583,6 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
       "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -605,7 +591,6 @@
       "version": "1.2.6",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
       "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.scandir": "2.1.4",
         "fastq": "^1.6.0"
@@ -5414,7 +5399,6 @@
       "version": "3.2.5",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
       "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
-      "dev": true,
       "dependencies": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
@@ -5443,7 +5427,6 @@
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
       "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
-      "dev": true,
       "dependencies": {
         "reusify": "^1.0.4"
       }
@@ -5830,6 +5813,45 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/glob-base": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+      "dependencies": {
+        "glob-parent": "^2.0.0",
+        "is-glob": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/glob-base/node_modules/glob-parent": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+      "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+      "dependencies": {
+        "is-glob": "^2.0.0"
+      }
+    },
+    "node_modules/glob-base/node_modules/is-extglob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/glob-base/node_modules/is-glob": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+      "dependencies": {
+        "is-extglob": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/glob-parent": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -6961,6 +6983,14 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/is-dotfile": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/is-error": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz",
@@ -8271,6 +8301,11 @@
       "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
       "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
     },
+    "node_modules/lodash.topath": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
+      "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
+    },
     "node_modules/lodash.trim": {
       "version": "4.5.1",
       "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
@@ -8607,7 +8642,6 @@
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
       "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true,
       "engines": {
         "node": ">= 8"
       }
@@ -10292,6 +10326,39 @@
         "safe-buffer": "^5.1.1"
       }
     },
+    "node_modules/parse-glob": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+      "dependencies": {
+        "glob-base": "^0.3.0",
+        "is-dotfile": "^1.0.0",
+        "is-extglob": "^1.0.0",
+        "is-glob": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse-glob/node_modules/is-extglob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+      "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse-glob/node_modules/is-glob": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+      "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+      "dependencies": {
+        "is-extglob": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/parse-json": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -11353,28 +11420,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/purgecss": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.2.tgz",
-      "integrity": "sha512-6J1zOEAZJX6VbfcaJHgdQf4uPhxVXvHz7dGgWYXLOI9q7QFZ5feh8NZ2+G3ysii/Sr8OyUe5yhQ5Z/xZ5gIRnQ==",
-      "dependencies": {
-        "commander": "^6.0.0",
-        "glob": "^7.0.0",
-        "postcss": "^8.2.1",
-        "postcss-selector-parser": "^6.0.2"
-      },
-      "bin": {
-        "purgecss": "bin/purgecss.js"
-      }
-    },
-    "node_modules/purgecss/node_modules/commander": {
-      "version": "6.2.1",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
-      "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
-      "engines": {
-        "node": ">= 6"
-      }
-    },
     "node_modules/qs": {
       "version": "6.2.3",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
@@ -11422,7 +11467,6 @@
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz",
       "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -11977,7 +12021,6 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true,
       "engines": {
         "iojs": ">=1.0.0",
         "node": ">=0.10.0"
@@ -12021,7 +12064,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
       "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
       "funding": [
         {
           "type": "github",
@@ -13624,30 +13666,37 @@
       }
     },
     "node_modules/tailwindcss": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.0.3.tgz",
-      "integrity": "sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.1.tgz",
+      "integrity": "sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ==",
       "dependencies": {
         "@fullhuman/postcss-purgecss": "^3.1.3",
         "bytes": "^3.0.0",
         "chalk": "^4.1.0",
+        "chokidar": "^3.5.1",
         "color": "^3.1.3",
         "detective": "^5.2.0",
         "didyoumean": "^1.2.1",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.2.5",
         "fs-extra": "^9.1.0",
         "html-tags": "^3.1.0",
-        "lodash": "^4.17.20",
+        "lodash": "^4.17.21",
+        "lodash.topath": "^4.5.2",
         "modern-normalize": "^1.0.0",
         "node-emoji": "^1.8.1",
+        "normalize-path": "^3.0.0",
         "object-hash": "^2.1.1",
+        "parse-glob": "^3.0.4",
         "postcss-functions": "^3",
         "postcss-js": "^3.0.3",
-        "postcss-nested": "^5.0.1",
+        "postcss-nested": "5.0.5",
         "postcss-selector-parser": "^6.0.4",
         "postcss-value-parser": "^4.1.0",
         "pretty-hrtime": "^1.0.3",
+        "quick-lru": "^5.1.1",
         "reduce-css-calc": "^2.1.8",
-        "resolve": "^1.19.0"
+        "resolve": "^1.20.0"
       },
       "bin": {
         "tailwind": "lib/cli.js",
@@ -13699,6 +13748,17 @@
         "purgecss": "bin/purgecss.js"
       }
     },
+    "node_modules/tailwindcss/node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/tapable": {
       "version": "0.1.10",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz",
@@ -15809,14 +15869,6 @@
         }
       }
     },
-    "@fullhuman/postcss-purgecss": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.2.tgz",
-      "integrity": "sha512-Qm8M89v4HVUTBQwuWREtzQojSqAbdp98kALMEw2NTrtrgHVqbLQrdiTBr0DAiNmG3hcqXSEexF/78uNUthMM9w==",
-      "requires": {
-        "purgecss": "^4.0.2"
-      }
-    },
     "@istanbuljs/load-nyc-config": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
@@ -15866,7 +15918,6 @@
       "version": "2.1.4",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
       "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==",
-      "dev": true,
       "requires": {
         "@nodelib/fs.stat": "2.0.4",
         "run-parallel": "^1.1.9"
@@ -15875,14 +15926,12 @@
     "@nodelib/fs.stat": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz",
-      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==",
-      "dev": true
+      "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q=="
     },
     "@nodelib/fs.walk": {
       "version": "1.2.6",
       "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz",
       "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==",
-      "dev": true,
       "requires": {
         "@nodelib/fs.scandir": "2.1.4",
         "fastq": "^1.6.0"
@@ -19652,7 +19701,6 @@
       "version": "3.2.5",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz",
       "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==",
-      "dev": true,
       "requires": {
         "@nodelib/fs.stat": "^2.0.2",
         "@nodelib/fs.walk": "^1.2.3",
@@ -19678,7 +19726,6 @@
       "version": "1.11.0",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz",
       "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==",
-      "dev": true,
       "requires": {
         "reusify": "^1.0.4"
       }
@@ -19948,6 +19995,38 @@
         "path-is-absolute": "^1.0.0"
       }
     },
+    "glob-base": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz",
+      "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=",
+      "requires": {
+        "glob-parent": "^2.0.0",
+        "is-glob": "^2.0.0"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz",
+          "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=",
+          "requires": {
+            "is-glob": "^2.0.0"
+          }
+        },
+        "is-extglob": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
+        },
+        "is-glob": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+          "requires": {
+            "is-extglob": "^1.0.0"
+          }
+        }
+      }
+    },
     "glob-parent": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -20787,6 +20866,11 @@
       "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
       "dev": true
     },
+    "is-dotfile": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
+      "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE="
+    },
     "is-error": {
       "version": "2.2.2",
       "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz",
@@ -21794,6 +21878,11 @@
       "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
       "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
     },
+    "lodash.topath": {
+      "version": "4.5.2",
+      "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz",
+      "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak="
+    },
     "lodash.trim": {
       "version": "4.5.1",
       "resolved": "https://registry.npmjs.org/lodash.trim/-/lodash.trim-4.5.1.tgz",
@@ -22055,8 +22144,7 @@
     "merge2": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
-      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
-      "dev": true
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
     },
     "micro-spelling-correcter": {
       "version": "1.1.1",
@@ -23351,6 +23439,32 @@
         "safe-buffer": "^5.1.1"
       }
     },
+    "parse-glob": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz",
+      "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=",
+      "requires": {
+        "glob-base": "^0.3.0",
+        "is-dotfile": "^1.0.0",
+        "is-extglob": "^1.0.0",
+        "is-glob": "^2.0.0"
+      },
+      "dependencies": {
+        "is-extglob": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz",
+          "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA="
+        },
+        "is-glob": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz",
+          "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=",
+          "requires": {
+            "is-extglob": "^1.0.0"
+          }
+        }
+      }
+    },
     "parse-json": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -24135,24 +24249,6 @@
         }
       }
     },
-    "purgecss": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-4.0.2.tgz",
-      "integrity": "sha512-6J1zOEAZJX6VbfcaJHgdQf4uPhxVXvHz7dGgWYXLOI9q7QFZ5feh8NZ2+G3ysii/Sr8OyUe5yhQ5Z/xZ5gIRnQ==",
-      "requires": {
-        "commander": "^6.0.0",
-        "glob": "^7.0.0",
-        "postcss": "^8.2.1",
-        "postcss-selector-parser": "^6.0.2"
-      },
-      "dependencies": {
-        "commander": {
-          "version": "6.2.1",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
-          "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
-        }
-      }
-    },
     "qs": {
       "version": "6.2.3",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz",
@@ -24184,8 +24280,7 @@
     "queue-microtask": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.2.tgz",
-      "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg==",
-      "dev": true
+      "integrity": "sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg=="
     },
     "quick-lru": {
       "version": "4.0.1",
@@ -24633,8 +24728,7 @@
     "reusify": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
-      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
-      "dev": true
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
     },
     "rimraf": {
       "version": "3.0.2",
@@ -24665,7 +24759,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
       "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
-      "dev": true,
       "requires": {
         "queue-microtask": "^1.2.2"
       }
@@ -26005,30 +26098,37 @@
       }
     },
     "tailwindcss": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.0.3.tgz",
-      "integrity": "sha512-s8NEqdLBiVbbdL0a5XwTb8jKmIonOuI4RMENEcKLR61jw6SdKvBss7NWZzwCaD+ZIjlgmesv8tmrjXEp7C0eAQ==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.1.tgz",
+      "integrity": "sha512-zZ6axGqpSZOCBS7wITm/WNHkBzDt5CIZlDlx0eCVldwTxFPELCVGbgh7Xpb3/kZp3cUxOmK7bZUjqhuMrbN6xQ==",
       "requires": {
         "@fullhuman/postcss-purgecss": "^3.1.3",
         "bytes": "^3.0.0",
         "chalk": "^4.1.0",
+        "chokidar": "^3.5.1",
         "color": "^3.1.3",
         "detective": "^5.2.0",
         "didyoumean": "^1.2.1",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.2.5",
         "fs-extra": "^9.1.0",
         "html-tags": "^3.1.0",
-        "lodash": "^4.17.20",
+        "lodash": "^4.17.21",
+        "lodash.topath": "^4.5.2",
         "modern-normalize": "^1.0.0",
         "node-emoji": "^1.8.1",
+        "normalize-path": "^3.0.0",
         "object-hash": "^2.1.1",
+        "parse-glob": "^3.0.4",
         "postcss-functions": "^3",
         "postcss-js": "^3.0.3",
-        "postcss-nested": "^5.0.1",
+        "postcss-nested": "5.0.5",
         "postcss-selector-parser": "^6.0.4",
         "postcss-value-parser": "^4.1.0",
         "pretty-hrtime": "^1.0.3",
+        "quick-lru": "^5.1.1",
         "reduce-css-calc": "^2.1.8",
-        "resolve": "^1.19.0"
+        "resolve": "^1.20.0"
       },
       "dependencies": {
         "@fullhuman/postcss-purgecss": {
@@ -26054,6 +26154,11 @@
             "postcss": "^8.2.1",
             "postcss-selector-parser": "^6.0.2"
           }
+        },
+        "quick-lru": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+          "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
         }
       }
     },
diff --git a/package.json b/package.json
index 340bf0f..b5c433c 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,6 @@
     "release": "np"
   },
   "dependencies": {
-    "@fullhuman/postcss-purgecss": "^4.0.0",
     "browser-sync": "^2.26.13",
     "color-shorthand-hex-to-six-digit": "^3.0.2",
     "email-comb": "^5.0.0",
@@ -65,7 +64,7 @@
     "prevent-widows": "^1.0.2",
     "query-string": "^6.13.7",
     "string-strip-html": "^8.2.0",
-    "tailwindcss": "^2.0.3",
+    "tailwindcss": "^2.1.1",
     "tailwindcss-box-shadow": "^1.0.0"
   },
   "devDependencies": {
diff --git a/src/generators/tailwindcss.js b/src/generators/tailwindcss.js
index 5ddd0e5..1ce552a 100644
--- a/src/generators/tailwindcss.js
+++ b/src/generators/tailwindcss.js
@@ -6,57 +6,25 @@ const atImport = require('postcss-import')
 const postcssNested = require('postcss-nested')
 const {requireUncached} = require('../utils/helpers')
 const mergeLonghand = require('postcss-merge-longhand')
-const purgecss = require('@fullhuman/postcss-purgecss')
 const {get, isObject, isEmpty, merge} = require('lodash')
 
 module.exports = {
   compile: async (css = '', html = '', tailwindConfig = {}, maizzleConfig = {}) => {
+    process.env.NODE_ENV = maizzleConfig.env || 'local'
+
     tailwindConfig = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : get(maizzleConfig, 'build.tailwind.config', 'tailwind.config.js')
     const tailwindConfigObject = (isObject(tailwindConfig) && !isEmpty(tailwindConfig)) ? tailwindConfig : requireUncached(path.resolve(process.cwd(), tailwindConfig))
-    const purgeCSSOptions = get(maizzleConfig, 'purgeCSS', {})
-
-    const buildTemplates = get(maizzleConfig, 'build.templates', [])
-    const templateSources = Array.isArray(buildTemplates) ? buildTemplates.map(({source}) => `${source}/**/*.*`) : [buildTemplates].map(({source}) => `${source}/**/*.*`)
-    const tailwindSources = Array.isArray(tailwindConfigObject.purge) ? tailwindConfigObject.purge : (isObject(tailwindConfigObject.purge) ? tailwindConfigObject.purge.content || [] : [])
-
-    const extraPurgeSources = purgeCSSOptions.content || []
-
-    const purgeSources = [
-      'src/layouts/**/*.*',
-      'src/partials/**/*.*',
-      'src/components/**/*.*',
-      ...templateSources,
-      ...tailwindSources,
-      ...extraPurgeSources,
-      {raw: html}
-    ]
-
-    const tailwindExtractor = content => {
-      // Capture as liberally as possible, including things like `h-(screen-1.5)`
-      const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
-
-      // Capture classes within other delimiters like .block(class="w-1/2") in Pug
-      const innerMatches = content.match(/[^<>"'`\s.()]*[^<>"'`\s.():]/g) || []
-
-      return [...broadMatches, ...innerMatches]
-    }
-
-    const extractor = get(tailwindConfigObject, 'purge.options.defaultExtractor') || purgeCSSOptions.defaultExtractor || tailwindExtractor
-    const purgeSafeList = get(tailwindConfigObject, 'purge.options.safelist') || purgeCSSOptions.safelist || {}
-    const purgeBlockList = get(tailwindConfigObject, 'purge.options.blocklist') || purgeCSSOptions.blocklist || []
-
-    const purgeCssPlugin = maizzleConfig.env === 'local' ? () => {} : purgecss({
-      content: purgeSources,
-      defaultExtractor: content => [...extractor(content)],
-      safelist: purgeSafeList,
-      blocklist: [...purgeBlockList]
-    })
-
-    const mergeLonghandPlugin = maizzleConfig.env === 'local' ? () => {} : mergeLonghand()
 
     const coreConfig = {
       important: true,
-      purge: false,
+      purge: {
+        enabled: maizzleConfig.env !== 'local',
+        content: [
+          'src/**/*.*',
+          {raw: html}
+        ],
+        options: get(maizzleConfig, 'purgeCSS', {})
+      },
       corePlugins: {
         animation: false,
         backgroundOpacity: false,
@@ -75,10 +43,6 @@ module.exports = {
       ]
     }
 
-    const tailwindPlugin = isEmpty(tailwindConfigObject) ? tailwindcss(coreConfig) : tailwindcss(merge(coreConfig, tailwindConfigObject))
-
-    const postcssUserPlugins = get(maizzleConfig, 'build.postcss.plugins', [])
-
     const userFilePath = get(maizzleConfig, 'build.tailwind.css')
 
     css = await fs.pathExists(userFilePath).then(async exists => {
@@ -87,16 +51,15 @@ module.exports = {
         return css + userFileCSS
       }
 
-      return `@tailwind components;\n ${css}\n @tailwind utilities;`
+      return css
     })
 
     return postcss([
       atImport({path: userFilePath ? path.dirname(userFilePath) : []}),
       postcssNested(),
-      tailwindPlugin,
-      purgeCssPlugin,
-      mergeLonghandPlugin,
-      ...postcssUserPlugins
+      tailwindcss(merge(coreConfig, tailwindConfigObject)),
+      maizzleConfig.env === 'local' ? () => {} : mergeLonghand(),
+      ...get(maizzleConfig, 'build.postcss.plugins', [])
     ])
       .process(css, {from: undefined})
       .then(result => result.css)


Test patch:
diff --git a/test/expected/tailwind/default.css b/test/expected/tailwind/default.css
deleted file mode 100644
index 8dd6dbd..0000000
--- a/test/expected/tailwind/default.css
+++ /dev/null
@@ -1,6 +0,0 @@
-@media (min-width: 1280px) {
-
-  .xl\:z-0 {
-    z-index: 0 !important
-  }
-}
diff --git a/test/test-tailwind.js b/test/test-tailwind.js
index 3d83c8e..e65f36f 100644
--- a/test/test-tailwind.js
+++ b/test/test-tailwind.js
@@ -1,25 +1,16 @@
 const test = require('ava')
-const fs = require('fs-extra')
 const Tailwind = require('../src/generators/tailwindcss')
 
 test('uses Tailwind defaults if no config specified', async t => {
-  const config = {
-    env: 'node',
-    purgeCSS: {
-      content: [{raw: '<div class="xl:z-0"></div>'}]
-    }
-  }
-
-  const css = await Tailwind.compile('', '', {}, config)
-  const expected = await fs.readFile('./test/expected/tailwind/default.css', 'utf8')
+  const css = await Tailwind.compile('@tailwind utilities', '<p class="xl:z-0"></p>', {}, {env: 'production'})
 
   t.not(css, undefined)
-  t.is(css.trim(), expected.trim())
+  t.true(css.includes('.xl\\:z-0'))
 })
 
 test('uses CSS file provided in environment config', async t => {
   const config = {
-    env: 'node',
+    env: 'production',
     build: {
       tailwind: {
         css: './test/stubs/main.css'
@@ -30,15 +21,13 @@ test('uses CSS file provided in environment config', async t => {
   const css = await Tailwind.compile('', '<div class="text-center foo">test</div>', {}, config)
 
   t.not(css, undefined)
-  t.is(css.trim(), '.text-center {\n  text-align: center !important;\n}\n\n.foo {\n  color: red;\n}')
+  t.true(css.includes('.text-center'))
+  t.true(css.includes('.foo'))
 })
 
-test('uses purgeCSS options provided in the config', async t => {
-  const html = '<div class="z-0 text-center"></div>'
-
+test('uses purgeCSS options provided in the maizzle config', async t => {
   const arrayConfig = {
     purgeCSS: {
-      content: [{raw: html}],
       safelist: ['z-10'],
       blocklist: ['text-center']
     }
@@ -46,7 +35,6 @@ test('uses purgeCSS options provided in the config', async t => {
 
   const objectConfig = {
     purgeCSS: {
-      content: [{raw: html}],
       safelist: {
         standard: ['z-10']
       },
@@ -54,16 +42,21 @@ test('uses purgeCSS options provided in the config', async t => {
     }
   }
 
-  const css1 = await Tailwind.compile('', '', {}, arrayConfig)
-  const css2 = await Tailwind.compile('', '', {}, objectConfig)
+  const css1 = await Tailwind.compile('@tailwind utilities', '<div class="z-0 text-center"></div>', {}, arrayConfig)
+  const css2 = await Tailwind.compile('@tailwind utilities', '<div class="z-0 text-center"></div>', {}, objectConfig)
+
+  t.true(css1.includes('.z-0'))
+  t.true(css1.includes('.z-10'))
+  t.false(css1.includes('.text-center'))
 
-  t.is(css1.trim(), '.z-0 {\n  z-index: 0 !important\n}\n\n.z-10 {\n  z-index: 10 !important\n}')
-  t.is(css2.trim(), '.z-0 {\n  z-index: 0 !important\n}\n\n.z-10 {\n  z-index: 10 !important\n}')
+  t.true(css2.includes('.z-0'))
+  t.true(css2.includes('.z-10'))
+  t.false(css2.includes('.text-center'))
 })
 
-test('uses postcss plugins from the config when compiling from string', async t => {
+test('uses postcss plugins from the maizzle config when compiling from string', async t => {
   const maizzleConfig = {
-    env: 'node',
+    env: 'production',
     build: {
       postcss: {
         plugins: [