Repository: tivac/modular-css
Pull request #877 (2022-07-26 07:00:28)

chore: lint website
Because https://ota-meshi.github.io/eslint-plugin-svelte/ is super-cool and useful.

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 28d6e12b939e5ad286779c7b87bb24e0b0abada2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/tivac/modular-css/new/linting?filename=.changeset/smart-taxis-unite.md&value=---%0A%22%40modular-css%2Fcss-to-js%22%3A%20patch%0A%22%40modular-css%2Fwww%22%3A%20patch%0A---%0A%0Achore%3A%20lint%20website%0A)


codesee-maps[bot]: <details>
<summary><b>👇 <a href="https://app.codesee.io/r/reviews?pr=877&src=https%3A%2F%2Fgithub.com%2Ftivac%2Fmodular-css">Click on the image</a> for a new way to code review</b>
</summary>

  - Make big changes easier — review code in small groups of related files
  - Know where to start — see the whole change at a glance
  - Take a code tour — explore the change with an interactive tour
  - Make comments and review — all fully sync’ed with github

    [Try it now!](https://app.codesee.io/r/reviews?pr=877&src=https%3A%2F%2Fgithub.com%2Ftivac%2Fmodular-css)
</details>

[![Review these changes using an interactive CodeSee Map](https://s3.us-east-2.amazonaws.com/maps.codesee.io/images/github/tivac/modular-css/877/1a5e939f/28d6e12b939e5ad286779c7b87bb24e0b0abada2.svg)](https://app.codesee.io/r/reviews?pr=877&src=https%3A%2F%2Fgithub.com%2Ftivac%2Fmodular-css)

#### Legend
![CodeSee Map Legend](https://app.codesee.io/Legend_v2.png)
netlify[bot]: ### <span aria-hidden="true">✅</span> Deploy Preview for *m-css* ready!


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 28d6e12b939e5ad286779c7b87bb24e0b0abada2 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/m-css/deploys/62df910ea65737000811902b |
|<span aria-hidden="true">😎</span> Deploy Preview | https://deploy-preview-877--m-css.netlify.app |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTg3Ny0tbS1jc3MubmV0bGlmeS5hcHAifQ.04eFrFfnKi2Iw76VpTFud0X41lNUw0xv5Y9d7EBJyM8)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---

_To edit notification comments on pull requests, go to your [Netlify site settings](https://app.netlify.com/sites/m-css/settings/deploys#deploy-notifications)._
codecov[bot]: # [Codecov](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit) Report
> Merging [#877](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit) (28d6e12) into [main](https://codecov.io/gh/tivac/modular-css/commit/4f83d073cc1e4c5b1ab58861ccc644e199d9f058?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit) (4f83d07) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##             main     #877   +/-   ##
=======================================
  Coverage   99.71%   99.71%           
=======================================
  Files          37       37           
  Lines        1417     1417           
  Branches      227      227           
=======================================
  Hits         1413     1413           
  Misses          4        4           
```


| [Impacted Files](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit) | Coverage Δ | |
|---|---|---|
| [packages/css-to-js/css-to-js.js](https://codecov.io/gh/tivac/modular-css/pull/877/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit#diff-cGFja2FnZXMvY3NzLXRvLWpzL2Nzcy10by1qcy5qcw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit). Last update [4f83d07...28d6e12](https://codecov.io/gh/tivac/modular-css/pull/877?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Pat+Cavit).

tivac: **TODO**: this broke the header css on the site in the deploy preview, I'll need to investigate that tomorrow.

Patch:
diff --git a/.changeset/clean-students-shave.md b/.changeset/clean-students-shave.md
new file mode 100644
index 00000000..cdd81a18
--- /dev/null
+++ b/.changeset/clean-students-shave.md
@@ -0,0 +1,7 @@
+---
+"@modular-css/svelte": minor
+---
+
+Finally added support for unquoted `class={css.foo}` references. This had been needed for a long time time and I'm happy it's finally here!
+
+Quoted `class="{css.foo}"` style conyinues to be supported as well, along with bare references both in template and `<script>` blocks. 
diff --git a/.eslintrc.js b/.eslintrc.js
index 8e03d22a..8f1e8e01 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -2,6 +2,7 @@ module.exports = {
     extends : [
         "@tivac",
         "plugin:jest/recommended",
+        "plugin:eslint-comments/recommended",
     ],
 
     parser : "@babel/eslint-parser",
@@ -39,5 +40,73 @@ module.exports = {
             { blankLine : "always", prev : "directive", next : "*" },
             { blankLine : "any", prev : "directive", next : "directive" },
         ],
+
+        "no-unused-vars" : [ "warn", {
+            varsIgnorePattern : "^_",
+            argsIgnorePattern : "^_",
+        }],
+
+        // Plugins
+        "eslint-comments/require-description" : "warn",
+        "eslint-comments/disable-enable-pair" : [ "warn", { allowWholeFile : true }],
     },
+
+    overrides : [{
+        files   : [ "packages/www/**/*" ],
+
+        extends : [
+            "plugin:@tivac/svelte/recommended",
+        ],
+    }, {
+        files   : [ "packages/www/**/*.svelte" ],
+
+        extends : [
+            "plugin:@tivac/svelte/recommended",
+            "plugin:svelte/recommended",
+        ],
+
+        // Parse the `<script>` in `.svelte` using babel's parser for import() support
+        parserOptions : {
+            parser : "@babel/eslint-parser",
+        },
+        
+        rules : {
+            // Disabled in favor of svelte/indent further down
+            indent : "off",
+
+            // Security
+
+            // Disabled because we use 'em to show markdown content
+            "svelte/no-at-html-tags" : "off",
+
+            // Best Practices
+            "svelte/no-useless-mustaches"              : "warn",
+
+            // Stylistic Issues
+            "svelte/require-optimized-style-attribute" : "error",
+            "svelte/first-attribute-linebreak"         : [ "warn", {
+                singleline : "beside",
+                multiline  : "below",
+            }],
+            "svelte/html-closing-bracket-spacing" : "warn",
+            "svelte/html-quotes"                  : [ "warn", {
+                prefer  : "double",
+            }],
+            "svelte/indent" : [ "warn", {
+                indent                    : 4,
+                indentScript              : false,
+                alignAttributesVertically : true,
+            }],
+            "svelte/max-attributes-per-line" : [ "warn", {
+                singleline : 3,
+            }],
+            "svelte/mustache-spacing"                          : "warn",
+            "svelte/no-spaces-around-equal-signs-in-attribute" : "warn",
+            "svelte/prefer-class-directive"                    : "off",
+            "svelte/prefer-style-directive"                    : "warn",
+            "svelte/shorthand-attribute"                       : "warn",
+            "svelte/shorthand-directive"                       : "warn",
+            "svelte/spaced-html-comment"                       : "warn",
+        },
+    }],
 };
diff --git a/package-lock.json b/package-lock.json
index 7f766b41..b0ed609f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,6 +25,7 @@
         "@rollup/plugin-commonjs": "^22.0.0",
         "@rollup/plugin-node-resolve": "^13.3.0",
         "@tivac/eslint-config": "^2.4.0",
+        "@tivac/eslint-plugin-svelte": "^3.1.0",
         "browserify": "^17.0.0",
         "cli-tester": "2.0.0",
         "codemirror": "^5.65.1",
@@ -34,7 +35,9 @@
         "dentist": "1.0.3",
         "env-cmd": "^10.0.1",
         "eslint": "^8.16.0",
+        "eslint-plugin-eslint-comments": "^3.2.0",
         "eslint-plugin-jest": "^26.4.6",
+        "eslint-plugin-svelte": "^2.3.0",
         "factor-bundle": "2.5.0",
         "from2-string": "1.1.0",
         "github-slugger": "^1.4.0",
@@ -2195,6 +2198,15 @@
       "integrity": "sha512-GfVD41ApT2UTjUQbRIbgFj65o6/Nycp3ggUbaJW99NP8xiAjdngdRx6IbCy2sRLPgrpnv8KipG1U9EfL97gqyA==",
       "dev": true
     },
+    "node_modules/@tivac/eslint-plugin-svelte": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@tivac/eslint-plugin-svelte/-/eslint-plugin-svelte-3.1.0.tgz",
+      "integrity": "sha512-MBfyid10inxb23LQLJRBqHNz1xRdw0k7gyOL22o8eJiNGh+pnrWMVwvbdtPrZYc5mVr1m1+X3pu2XkVeEKHXcg==",
+      "dev": true,
+      "peerDependencies": {
+        "eslint": ">= 7"
+      }
+    },
     "node_modules/@trysound/sax": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -5131,6 +5143,34 @@
         "url": "https://opencollective.com/eslint"
       }
     },
+    "node_modules/eslint-plugin-eslint-comments": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz",
+      "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==",
+      "dev": true,
+      "dependencies": {
+        "escape-string-regexp": "^1.0.5",
+        "ignore": "^5.0.5"
+      },
+      "engines": {
+        "node": ">=6.5.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": ">=4.19.1"
+      }
+    },
+    "node_modules/eslint-plugin-eslint-comments/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
     "node_modules/eslint-plugin-jest": {
       "version": "26.6.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.6.0.tgz",
@@ -5155,6 +5195,37 @@
         }
       }
     },
+    "node_modules/eslint-plugin-svelte": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.3.0.tgz",
+      "integrity": "sha512-yPbbqGz6e2jbYla4IJTXg1MTFVPnPvJDDhkaGLRzQg9u6czIdRHKpE/nx0S7q3r0ZkbfCRq+T5Hhk69Qg+0LlA==",
+      "dev": true,
+      "dependencies": {
+        "debug": "^4.3.1",
+        "eslint-utils": "^3.0.0",
+        "known-css-properties": "^0.25.0",
+        "postcss": "^8.4.5",
+        "postcss-load-config": "^3.1.4",
+        "postcss-safe-parser": "^6.0.0",
+        "sourcemap-codec": "^1.4.8",
+        "svelte-eslint-parser": "^0.17.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      },
+      "peerDependencies": {
+        "eslint": "^7.0.0 || ^8.0.0-0",
+        "svelte": "^3.37.0"
+      },
+      "peerDependenciesMeta": {
+        "svelte": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/eslint-scope": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -9013,6 +9084,12 @@
         "node": ">=6"
       }
     },
+    "node_modules/known-css-properties": {
+      "version": "0.25.0",
+      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
+      "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==",
+      "dev": true
+    },
     "node_modules/labeled-stream-splicer": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz",
@@ -10663,6 +10740,35 @@
         "postcss": "^8.2.15"
       }
     },
+    "node_modules/postcss-load-config": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+      "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+      "dev": true,
+      "dependencies": {
+        "lilconfig": "^2.0.5",
+        "yaml": "^1.10.2"
+      },
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      },
+      "peerDependencies": {
+        "postcss": ">=8.0.9",
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "postcss": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/postcss-merge-longhand": {
       "version": "5.1.6",
       "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz",
@@ -10961,6 +11067,22 @@
         "postcss": "^8.2.15"
       }
     },
+    "node_modules/postcss-safe-parser": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
+      "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      },
+      "peerDependencies": {
+        "postcss": "^8.3.3"
+      }
+    },
     "node_modules/postcss-selector-parser": {
       "version": "6.0.10",
       "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
@@ -13176,6 +13298,57 @@
         "node": ">= 8"
       }
     },
+    "node_modules/svelte-eslint-parser": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.17.0.tgz",
+      "integrity": "sha512-hHuB9upgGBNIMb7mL2UyGHxnNitQqWAB3GojHtN2w+kTH4jmZmNCHR4aHln8AjXQuQ2+rYTyVS0LeBPXL+ZrDQ==",
+      "dev": true,
+      "dependencies": {
+        "eslint-scope": "^7.0.0",
+        "eslint-visitor-keys": "^3.0.0",
+        "espree": "^9.0.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ota-meshi"
+      },
+      "peerDependencies": {
+        "svelte": "^3.37.0"
+      }
+    },
+    "node_modules/svelte-eslint-parser/node_modules/eslint-scope": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+      "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+      "dev": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+      "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/svelte-eslint-parser/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
     "node_modules/svelte-hmr": {
       "version": "0.14.12",
       "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.12.tgz",
@@ -16538,6 +16711,13 @@
       "integrity": "sha512-GfVD41ApT2UTjUQbRIbgFj65o6/Nycp3ggUbaJW99NP8xiAjdngdRx6IbCy2sRLPgrpnv8KipG1U9EfL97gqyA==",
       "dev": true
     },
+    "@tivac/eslint-plugin-svelte": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@tivac/eslint-plugin-svelte/-/eslint-plugin-svelte-3.1.0.tgz",
+      "integrity": "sha512-MBfyid10inxb23LQLJRBqHNz1xRdw0k7gyOL22o8eJiNGh+pnrWMVwvbdtPrZYc5mVr1m1+X3pu2XkVeEKHXcg==",
+      "dev": true,
+      "requires": {}
+    },
     "@trysound/sax": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@@ -18931,6 +19111,24 @@
         }
       }
     },
+    "eslint-plugin-eslint-comments": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz",
+      "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5",
+        "ignore": "^5.0.5"
+      },
+      "dependencies": {
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+          "dev": true
+        }
+      }
+    },
     "eslint-plugin-jest": {
       "version": "26.6.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.6.0.tgz",
@@ -18940,6 +19138,22 @@
         "@typescript-eslint/utils": "^5.10.0"
       }
     },
+    "eslint-plugin-svelte": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.3.0.tgz",
+      "integrity": "sha512-yPbbqGz6e2jbYla4IJTXg1MTFVPnPvJDDhkaGLRzQg9u6czIdRHKpE/nx0S7q3r0ZkbfCRq+T5Hhk69Qg+0LlA==",
+      "dev": true,
+      "requires": {
+        "debug": "^4.3.1",
+        "eslint-utils": "^3.0.0",
+        "known-css-properties": "^0.25.0",
+        "postcss": "^8.4.5",
+        "postcss-load-config": "^3.1.4",
+        "postcss-safe-parser": "^6.0.0",
+        "sourcemap-codec": "^1.4.8",
+        "svelte-eslint-parser": "^0.17.0"
+      }
+    },
     "eslint-scope": {
       "version": "5.1.1",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -21724,6 +21938,12 @@
       "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
       "dev": true
     },
+    "known-css-properties": {
+      "version": "0.25.0",
+      "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz",
+      "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==",
+      "dev": true
+    },
     "labeled-stream-splicer": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.2.tgz",
@@ -22856,6 +23076,16 @@
       "dev": true,
       "requires": {}
     },
+    "postcss-load-config": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+      "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+      "dev": true,
+      "requires": {
+        "lilconfig": "^2.0.5",
+        "yaml": "^1.10.2"
+      }
+    },
     "postcss-merge-longhand": {
       "version": "5.1.6",
       "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz",
@@ -23037,6 +23267,13 @@
         "postcss-value-parser": "^4.2.0"
       }
     },
+    "postcss-safe-parser": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
+      "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+      "dev": true,
+      "requires": {}
+    },
     "postcss-selector-parser": {
       "version": "6.0.10",
       "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
@@ -24770,6 +25007,41 @@
       "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.49.0.tgz",
       "integrity": "sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA=="
     },
+    "svelte-eslint-parser": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.17.0.tgz",
+      "integrity": "sha512-hHuB9upgGBNIMb7mL2UyGHxnNitQqWAB3GojHtN2w+kTH4jmZmNCHR4aHln8AjXQuQ2+rYTyVS0LeBPXL+ZrDQ==",
+      "dev": true,
+      "requires": {
+        "eslint-scope": "^7.0.0",
+        "eslint-visitor-keys": "^3.0.0",
+        "espree": "^9.0.0"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "7.1.1",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+          "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.3.0",
+            "estraverse": "^5.2.0"
+          }
+        },
+        "eslint-visitor-keys": {
+          "version": "3.3.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+          "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+          "dev": true
+        },
+        "estraverse": {
+          "version": "5.3.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+          "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+          "dev": true
+        }
+      }
+    },
     "svelte-hmr": {
       "version": "0.14.12",
       "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.14.12.tgz",
diff --git a/package.json b/package.json
index c9393b54..a23f3e00 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
     "@rollup/plugin-commonjs": "^22.0.0",
     "@rollup/plugin-node-resolve": "^13.3.0",
     "@tivac/eslint-config": "^2.4.0",
+    "@tivac/eslint-plugin-svelte": "^3.1.0",
     "browserify": "^17.0.0",
     "cli-tester": "2.0.0",
     "codemirror": "^5.65.1",
@@ -47,7 +48,9 @@
     "dentist": "1.0.3",
     "env-cmd": "^10.0.1",
     "eslint": "^8.16.0",
+    "eslint-plugin-eslint-comments": "^3.2.0",
     "eslint-plugin-jest": "^26.4.6",
+    "eslint-plugin-svelte": "^2.3.0",
     "factor-bundle": "2.5.0",
     "from2-string": "1.1.0",
     "github-slugger": "^1.4.0",
diff --git a/packages/cli/cli.js b/packages/cli/cli.js
index 133090be..fd9e92d3 100755
--- a/packages/cli/cli.js
+++ b/packages/cli/cli.js
@@ -1,4 +1,5 @@
 #!/usr/bin/env node
+/* eslint-disable no-console -- CLIs can write to console! */
 "use strict";
 
 const fs   = require("fs");
@@ -71,7 +72,6 @@ glob({
     return process.stdout.write(`${css}\n`);
 })
 .catch((error) => {
-    // eslint-disable-next-line no-console
     console.log(error);
 
     process.stderr.write(error.toString());
diff --git a/packages/css-to-js/css-to-js.js b/packages/css-to-js/css-to-js.js
index 78f6afe3..f67dccec 100644
--- a/packages/css-to-js/css-to-js.js
+++ b/packages/css-to-js/css-to-js.js
@@ -1,4 +1,3 @@
-/* eslint-disable max-statements */
 "use strict";
 
 const path = require("path");
@@ -53,9 +52,10 @@ const esm = (key, value) => {
     return safeKey === safeValue ? safeKey : `${safeKey} as ${safeValue}`;
 };
 
+// eslint-disable-next-line max-statements -- too hard to split out
 exports.transform = (file, processor, opts = {}) => {
     // Remove processor from opts so just-extend doesn't recurse forever
-    const { processor : optcessor, ..._opts } = opts;
+    const { processor : _optcessor, ..._opts } = opts;
 
     const options = extend(true, Object.create(null), DEFAULTS, _opts);
 
diff --git a/packages/processor/plugins/before/graph-nodes.js b/packages/processor/plugins/before/graph-nodes.js
index ba32bef5..8739153f 100644
--- a/packages/processor/plugins/before/graph-nodes.js
+++ b/packages/processor/plugins/before/graph-nodes.js
@@ -44,7 +44,7 @@ module.exports = () => ({
             }
 
             if(rule.type !== "decl") {
-                // eslint-disable-next-line consistent-return
+                // eslint-disable-next-line consistent-return -- this seems fine
                 return processor._addDependency({
                     dependency,
                     refs,
diff --git a/packages/processor/plugins/values-import.js b/packages/processor/plugins/values-import.js
index 56e18ed5..bb5a2f38 100644
--- a/packages/processor/plugins/values-import.js
+++ b/packages/processor/plugins/values-import.js
@@ -14,7 +14,7 @@ module.exports = () => ({
 
         return {
             AtRule : {
-                // eslint-disable-next-line max-statements
+                // eslint-disable-next-line max-statements -- too much state passing to extract
                 value(rule) {
                     const parsed = parser.parse(rule.params);
                     const file = processor.resolve(from, parsed.source);
diff --git a/packages/processor/processor.js b/packages/processor/processor.js
index d71feec8..50827e65 100644
--- a/packages/processor/processor.js
+++ b/packages/processor/processor.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-await-in-loop */
 "use strict";
 
 const path = require("path");
@@ -83,7 +82,6 @@ const DEFAULTS = {
 
 class Processor {
     constructor(opts = {}) {
-        /* eslint max-statements: [ "warn", 25 ] */
         const options = {
             __proto__ : null,
             ...DEFAULTS,
@@ -107,11 +105,8 @@ class Processor {
                 `mc${slug(relative(options.cwd, file))}_${selector}`;
         }
 
-        this._log = options.verbose ?
-            // eslint-disable-next-line no-console
-            console.log.bind(console, "[processor]") :
-            // eslint-disable-next-line no-empty-function
-            () => {};
+        // eslint-disable-next-line no-console, no-empty-function -- logging!
+        this._log = options.verbose ? console.log.bind(console, "[processor]") : () => {};
 
         this._loadFile = options.loadFile;
 
@@ -295,8 +290,10 @@ class Processor {
         for(const dep of files) {
             this._log("_after()", dep);
 
+            // eslint-disable-next-line no-await-in-loop -- we're ok
             await this._files[dep].walked;
 
+            // eslint-disable-next-line no-await-in-loop -- we got this
             const result = await this._after.process(
                 // NOTE: the call to .clone() is really important here, otherwise this call
                 // modifies the .result root itself and you process URLs multiple times
@@ -513,7 +510,7 @@ class Processor {
             const other = this._ids.get(check);
 
             if(other && other !== id) {
-                // eslint-disable-next-line no-console
+                // eslint-disable-next-line no-console -- warning
                 console.warn(`POTENTIAL DUPLICATE FILES:\n\t${relative(this._options.cwd, other)}\n\t${relative(this._options.cwd, id)}`);
             }
         }
@@ -541,6 +538,7 @@ class Processor {
                 );
             }
 
+            // eslint-disable-next-line no-await-in-loop -- it's cool
             file.result = await file.processed;
 
             const { result } = file;
diff --git a/packages/rollup-rewriter/rewriter.js b/packages/rollup-rewriter/rewriter.js
index 12162cc8..bb45a277 100644
--- a/packages/rollup-rewriter/rewriter.js
+++ b/packages/rollup-rewriter/rewriter.js
@@ -34,7 +34,7 @@ module.exports = (opts = {}) => {
         throw new Error("options.loadfn must be configured");
     }
 
-    // eslint-disable-next-line no-console, no-empty-function
+    // eslint-disable-next-line no-console, no-empty-function -- logging
     const log = options.verbose ? console.log.bind(console, "[rewriter]") : () => {};
 
     return {
diff --git a/packages/rollup/rollup.js b/packages/rollup/rollup.js
index f837fa94..0c578ec7 100644
--- a/packages/rollup/rollup.js
+++ b/packages/rollup/rollup.js
@@ -1,4 +1,3 @@
-/* eslint-disable max-statements, complexity */
 "use strict";
 
 const path = require("path");
@@ -56,7 +55,7 @@ module.exports = (
 
     const filter = utils.createFilter(options.include, options.exclude);
 
-    // eslint-disable-next-line no-console, no-empty-function
+    // eslint-disable-next-line no-console, no-empty-function -- logging
     const log = options.verbose ? console.log.bind(console, "[rollup]") : () => { };
 
     // istanbul ignore if: too hard to test this w/ defaults
@@ -144,6 +143,7 @@ module.exports = (
             };
         },
 
+        // eslint-disable-next-line max-statements, complexity -- too much state to extract
         async generateBundle(outputOptions, bundle) {
             // styleExport disables all output file generation
             if(options.styleExport) {
@@ -227,7 +227,7 @@ module.exports = (
             const duds = new Set();
 
             for(const [ entry, { deps, name }] of chunks) {
-                /* eslint-disable-next-line no-await-in-loop */
+                // eslint-disable-next-line no-await-in-loop -- has to happen in order
                 const result = await processor.output({
                     // Can't use this.getAssetFileName() here, because the source hasn't been set yet
                     //  Have to do our best to come up with a valid final location though...
diff --git a/packages/svelte/link.js b/packages/svelte/link.js
index 218a25f1..31e126eb 100644
--- a/packages/svelte/link.js
+++ b/packages/svelte/link.js
@@ -7,7 +7,7 @@ const { replaceTrailingNewlines } = require("./replacer.js");
 const linkRegex = /<link\b[^<>]*?\bhref=\s*(?:"([^"]+)"|'([^']+)'|([^>\s]+))[^>]*>/gm;
 const scriptRegex = /<script[^>]*?>([\S\s]*?)<\/script[^>]*?>/im;
 
-// eslint-disable-next-line max-statements
+// eslint-disable-next-line max-statements -- complex, oh well
 exports.extractLink = async ({
     source,
     file,
@@ -40,7 +40,7 @@ exports.extractLink = async ({
         }
 
         if(!filter(href)) {
-            // eslint-disable-next-line no-console
+            // eslint-disable-next-line no-console -- warningggggg
             console.warn(`Possible invalid <link> href: ${href}`);
         }
 
diff --git a/packages/svelte/replacer.js b/packages/svelte/replacer.js
index ee152ab2..7fc2fb6e 100644
--- a/packages/svelte/replacer.js
+++ b/packages/svelte/replacer.js
@@ -2,31 +2,43 @@
 
 const escape = require("escape-string-regexp");
 
-exports.replacer = (source, { identifier, lookup, keys }) => {
+exports.replacer = (source, { identifier, lookup, keys, classAttr } = false) => {
     const ids = keys.map(escape).join("|");
     const ident = escape(identifier);
 
-    return source
+    if(classAttr) {
+        // Replace class={<identifier>.<key>} values
+        source = source.replace(
+            new RegExp(`(class=)("|')?{${ident}\\.(${ids})}("|')?`, "gm"),
+            // eslint-disable-next-line max-params -- necessary due to number of matches
+            (match, before, quote1, key, quote2) => {
+                const replacement = quote1 ? lookup[key] : JSON.stringify(lookup[key]);
+
+                return `${before}${quote1 || ""}${replacement}${quote2 || ""}`;
+            }
+        );
+    } else {
         // Replace {<identifier>.<key>} values
         // Note extra exclusion to avoid accidentally matching ${<identifier>.<key>}
-        .replace(
+        source = source.replace(
             new RegExp(`([^$]){${ident}\\.(${ids})}`, "gm"),
             (match, before, key) => {
                 const replacement = lookup[key];
 
                 return `${before}${replacement}`;
             }
-        )
+        );
+    }
 
-        // Then any remaining <identifier>.<key> values
-        .replace(
-            new RegExp(`(\\b)${ident}\\.(${ids})(\\b)`, "gm"),
-            (match, before, key, suffix) => {
-                const replacement = lookup[key];
+    // Then any remaining <identifier>.<key> values
+    return source.replace(
+        new RegExp(`(\\b)${ident}\\.(${ids})(\\b)`, "gm"),
+        (match, before, key, suffix) => {
+            const replacement = lookup[key];
 
-                return `${before}"${replacement}"${suffix}`;
-            }
-        );
+            return `${before}"${replacement}"${suffix}`;
+        }
+    );
 };
 
 exports.replaceTrailingNewlines = (source, search) => source.replace(new RegExp(`${escape(search)}(?:\r?\n)*`), "");
diff --git a/packages/svelte/script.js b/packages/svelte/script.js
index d85beba3..4ccd1769 100644
--- a/packages/svelte/script.js
+++ b/packages/svelte/script.js
@@ -5,7 +5,7 @@ const parseImports = require("parse-es6-imports");
 
 const scriptRegex = /<script[^>]*?>(?<contents>[\S\s]*?)<\/script[^>]*?>/gim;
 
-// eslint-disable-next-line max-statements
+// eslint-disable-next-line max-statements -- it's complicated, just deal
 exports.extractImport = async ({
     source,
     filename,
diff --git a/packages/svelte/svelte.js b/packages/svelte/svelte.js
index 28f13a4d..e9b69289 100644
--- a/packages/svelte/svelte.js
+++ b/packages/svelte/svelte.js
@@ -39,10 +39,10 @@ module.exports = (opts = {}) => {
 
     const { cwd } = processor.options;
 
-    // eslint-disable-next-line no-console, no-empty-function
+    // eslint-disable-next-line no-console, no-empty-function -- logging
     const log = options.verbose ? console.log.bind(console, prefix) : () => {};
 
-    // eslint-disable-next-line no-console
+    // eslint-disable-next-line no-console -- warning
     const warn = console.warn.bind(console, prefix, "WARN");
 
     const relative = (file) => slash(path.relative(cwd, file));
@@ -81,7 +81,7 @@ module.exports = (opts = {}) => {
     // This function is hilariously large but it's actually simpler this way
     // Mostly because markup() is async so tracking state is painful w/o inlining
     // the whole damn thing
-    // eslint-disable-next-line max-statements
+    // eslint-disable-next-line max-statements -- just deal
     const markup = async ({ content, filename }) => {
         const file = filename ? relative(filename) : "Unknown file";
 
@@ -107,7 +107,7 @@ module.exports = (opts = {}) => {
 
         for(const parser of PARSING_ORDER) {
             try {
-                // eslint-disable-next-line no-await-in-loop
+                // eslint-disable-next-line no-await-in-loop -- fine fine I got this
                 ({ source, result, css, dependencies, ident = "css" } = await parser(searchBucket));
             } catch(e) {
                 throw e;
@@ -139,19 +139,9 @@ module.exports = (opts = {}) => {
             log("updating source {css.<key>} references from", css);
             log(JSON.stringify(classKeys));
             
-            const selectors = [ ...classKeys, ...valueKeys ].map(escape).join("|");
-
-            // Look for instances of class={css.foo} to warn about
-            const matches = source.match(new RegExp(`class={${escape(ident)}\\.(?:${selectors})}`, "g"));
-
-            if(matches) {
-                for(const match of matches) {
-                    warn(`Unquoted class attribute! ${match}`, file);
-                }
-            }
-
-            // Replace css.<key> values
+            // Replace class={css.<key>} values
             source = replacer(source, {
+                classAttr  : true,
                 identifier : ident,
                 keys       : classKeys,
                 lookup     : classKeys.reduce((acc, curr) => {
diff --git a/packages/vite/vite.js b/packages/vite/vite.js
index 4be2ab57..0a22b232 100644
--- a/packages/vite/vite.js
+++ b/packages/vite/vite.js
@@ -49,7 +49,7 @@ module.exports = (
 
     const { graph } = processor;
 
-    // eslint-disable-next-line no-console, no-empty-function
+    // eslint-disable-next-line no-console, no-empty-function -- logging is fine chill out
     const log = options.verbose ? console.log.bind(console, "[@mcss/vite]") : () => { };
 
     // istanbul ignore if: too hard to test this w/ defaults
diff --git a/packages/www/src/lib/title.svelte b/packages/www/src/lib/title.svelte
index 8f6f3d47..ad0012e3 100644
--- a/packages/www/src/lib/title.svelte
+++ b/packages/www/src/lib/title.svelte
@@ -1,7 +1,7 @@
+<script>
+export let title = false;
+</script>
+
 <svelte:head>
-	<title>modular-css{title ? ` | ${title}` : ""}</title>
+    <title>modular-css{title ? ` | ${title}` : ""}</title>
 </svelte:head>
-
-<script>
-    export let title = false;
-</script>
\ No newline at end of file
diff --git a/packages/www/src/routes/__layout.svelte b/packages/www/src/routes/__layout.svelte
index ce8e1ce1..e7295f45 100644
--- a/packages/www/src/routes/__layout.svelte
+++ b/packages/www/src/routes/__layout.svelte
@@ -1,25 +1,24 @@
-<link href="./_layout/layout.mcss" />
+<script context="module">
+import { page } from "$app/stores";
+</script>
+
+<script>
+import Header from "./_layout/header.svelte";
+import css from "./_layout/layout.mcss";
+
+$: ({ toc, fullscreen } = $page.stuff);
+</script>
 
-<div class="{css.page}" data-fullscreen="{fullscreen}">
+<div class={css.page} data-fullscreen={fullscreen}>
     <Header />
 
     {#if toc}
-    <nav class="{css.toc}">
-        {@html toc}
-    </nav>
+        <nav class={css.toc}>
+            {@html toc}
+        </nav>
     {/if}
 
-    <main class="{css.content}">
+    <main class={css.content}>
         <slot />
     </main>
 </div>
-
-<script context="module">
-import { page } from "$app/stores";
-</script>
-
-<script>
-import Header from "./_layout/header.svelte";
-
-$: ({ toc, fullscreen } = $page.stuff);
-</script>
\ No newline at end of file
diff --git a/packages/www/src/routes/_layout/header.svelte b/packages/www/src/routes/_layout/header.svelte
index 922c2902..30ada82e 100644
--- a/packages/www/src/routes/_layout/header.svelte
+++ b/packages/www/src/routes/_layout/header.svelte
@@ -1,20 +1,7 @@
-<link href="./header.mcss" />
-
-<div class="{css.header}">
-    <a href="/" class="{css.name}">modular-css</a>
-    <a href="/changelog/" class="{css.version}">v{version}</a>
-    
-    <div class="{css.links}">
-    {#each links as { text, url, external }}
-        <a href="{url}" class="{css.link}">{text}</a>
-    {/each}
-    </div>
-</div>
-
 <script>
 import { version } from "../../../package.json";
-// import css from "./header.mcss";
-
+import css from "./header.mcss";
+    
 const links = [
     {
         text : "Overview",
@@ -34,3 +21,15 @@ const links = [
     },
 ];
 </script>
+
+<div class={css.header}>
+    <a href="/" class={css.name}>modular-css</a>
+    <a href="/changelog/" class={css.version}>v{version}</a>
+    
+    <div class={css.links}>
+        {#each links as { text, url }}
+            <a href={url} class={css.link}>{text}</a>
+        {/each}
+    </div>
+</div>
+
diff --git a/packages/www/src/routes/api/index.svelte b/packages/www/src/routes/api/index.svelte
index 9da592ac..9bc26eda 100644
--- a/packages/www/src/routes/api/index.svelte
+++ b/packages/www/src/routes/api/index.svelte
@@ -1,11 +1,3 @@
-<link href="../_layout/layout.mcss" />
-
-<Title title="API" />
-
-<div class="{css.prose}">
-    {@html content}
-</div>
-
 <script context="module">
 import { content, toc } from "./_content/api.md";
 
@@ -21,4 +13,12 @@ export function load() {
 
 <script>
 import Title from "$lib/title.svelte";
-</script>
\ No newline at end of file
+
+import css from "../_layout/layout.mcss";
+</script>
+
+<Title title="API" />
+
+<div class={css.prose}>
+    {@html content}
+</div>
diff --git a/packages/www/src/routes/changelog/index.svelte b/packages/www/src/routes/changelog/index.svelte
index 09ce5293..e3434f60 100644
--- a/packages/www/src/routes/changelog/index.svelte
+++ b/packages/www/src/routes/changelog/index.svelte
@@ -1,13 +1,13 @@
-<link href="./_changelog.mcss" />
-
-<Title title="Changelog" />
-
-<div class="{css.changelog}">
-    {@html content}
-</div>
-
 <script>
 import Title from "$lib/title.svelte";
 
 import content from "../../../../../CHANGELOG.md";
+
+import css from "./_changelog.mcss";
 </script>
+
+<Title title="Changelog" />
+
+<div class={css.changelog}>
+    {@html content}
+</div>
diff --git a/packages/www/src/routes/guide.svelte b/packages/www/src/routes/guide.svelte
index c7761f2c..a68d47c0 100644
--- a/packages/www/src/routes/guide.svelte
+++ b/packages/www/src/routes/guide.svelte
@@ -1,9 +1,9 @@
 <script context="module">
-    /* Just a redirect to /overview */
-    export async function load() {
-        return {
-            status: 301,
-            redirect: "/overview/",
-        };
+/* Just a redirect to /overview */
+export async function load() {
+    return {
+        status   : 301,
+        redirect : "/overview/",
     };
-</script>
\ No newline at end of file
+}
+</script>
diff --git a/packages/www/src/routes/index.svelte b/packages/www/src/routes/index.svelte
index 6ee9708a..34a3f6f7 100644
--- a/packages/www/src/routes/index.svelte
+++ b/packages/www/src/routes/index.svelte
@@ -1,30 +1,3 @@
-<link href="./_home/home.mcss" />
-
-<Title />
-
-<div class="{css.home}">
-    <div class="{css.title}">
-        <h1 class="{css.head}">modular-css</h1>
-        <h2 class="{css.headline}">CSS Modules, improved and extended.</h2>
-    </div>
-
-    <div class="{css.intro}">
-        <div class="{css.introtext}">
-            {@html intro}
-        </div>
-
-        <div class="{css.learn}">
-            <a href="/overview/" class="{css.cta}">
-                Learn More Now!
-            </a>
-        </div>
-    </div>
-
-    <div class="{css.example}">
-        {@html example}
-    </div>
-</div>
-
 <script context="module">
 export function load() {
     return {
@@ -40,5 +13,31 @@ import Title from "$lib/title.svelte";
 
 import { content as intro } from "./_home/intro.md";
 import { content as example } from "./_home/example.md";
+
+import css from "./_home/home.mcss";
 </script>
 
+<Title />
+
+<div class={css.home}>
+    <div class={css.title}>
+        <h1 class={css.head}>modular-css</h1>
+        <h2 class={css.headline}>CSS Modules, improved and extended.</h2>
+    </div>
+
+    <div class={css.intro}>
+        <div class={css.introtext}>
+            {@html intro}
+        </div>
+
+        <div class={css.learn}>
+            <a href="/overview/" class={css.cta}>
+                Learn More Now!
+            </a>
+        </div>
+    </div>
+
+    <div class={css.example}>
+        {@html example}
+    </div>
+</div>
diff --git a/packages/www/src/routes/overview/index.svelte b/packages/www/src/routes/overview/index.svelte
index d1fcc406..4094deb8 100644
--- a/packages/www/src/routes/overview/index.svelte
+++ b/packages/www/src/routes/overview/index.svelte
@@ -1,11 +1,3 @@
-<link href="../_layout/layout.mcss" />
-
-<Title title="Overview" />
-
-<div class="{css.prose}">
-    {@html content}
-</div>
-
 <script context="module">
 import { content, toc } from "./_content/intro.md";
 
@@ -20,4 +12,13 @@ export function load() {
 
 <script>
 import Title from "$lib/title.svelte";
-</script>
\ No newline at end of file
+
+import css from "../_layout/layout.mcss";
+</script>
+
+<Title title="Overview" />
+
+<div class={css.prose}>
+    {@html content}
+</div>
+
diff --git a/packages/www/src/routes/repl/_editor/codemirror.svelte b/packages/www/src/routes/repl/_editor/codemirror.svelte
index c1cd48a1..13390e05 100644
--- a/packages/www/src/routes/repl/_editor/codemirror.svelte
+++ b/packages/www/src/routes/repl/_editor/codemirror.svelte
@@ -1,21 +1,9 @@
-<link href="./codemirror.mcss" />
-
-{#await loading}
-    {#if slow}
-    <div class="{css.loading}">Loading editor...</div>
-    {/if}
-{:then _}
-    <div class="{css.editor}">
-        <textarea bind:this={textarea}></textarea>
-    </div>
-{:catch err}
-    <div class="{css.loaderror}">Unable to load editor</div>
-{/await}
-
 <script>
 import { onMount, onDestroy, createEventDispatcher } from "svelte";
 import debounce from "debounce";
 
+import css from "./codemirror.mcss";
+
 const loading = import("./codemirror.js");
 
 const dispatch = createEventDispatcher();
@@ -37,9 +25,9 @@ export async function input(src = "") {
     }
     
     editor.setValue(src);
-};
+}
 
-export async function error(error) {
+export async function error(err) {
     await loading;
     
     if(clearError) {
@@ -47,11 +35,11 @@ export async function error(error) {
         clearError = null;
     }
 
-    const { line, column, source } = error;
+    const { line, column, source } = err;
 
     // Only try and highlight lines if the necessary info exists
     if(typeof line === "undefined" || typeof column === "undefined") {
-        throw error;
+        throw err;
     }
     
     // Mark the offending line
@@ -70,7 +58,7 @@ export async function error(error) {
 
         marker.clear();
     };
-};
+}
 
 onMount(async () => {
     // Instantiate codemirror once it's loaded
@@ -81,11 +69,11 @@ onMount(async () => {
     ticking = false;
 
     editor = codemirror.fromTextArea(textarea, {
-        lineNumbers : true,
+        lineNumbers  : true,
         lineWrapping : true,
-        indentUnit : 4,
-        mode : "text/modular-css",
-        theme : "nord",
+        indentUnit   : 4,
+        mode         : "text/modular-css",
+        theme        : "nord",
     });
 
     editor.on("change", debounce(() => {
@@ -109,3 +97,15 @@ onDestroy(() => {
     editor = null;
 });
 </script>
+    
+{#await loading}
+    {#if slow}
+        <div class={css.loading}>Loading editor...</div>
+    {/if}
+{:then _loaded}
+    <div class={css.editor}>
+        <textarea bind:this={textarea}></textarea>
+    </div>
+{:catch _err}
+    <div class={css.loaderror}>Unable to load editor</div>
+{/await}
diff --git a/packages/www/src/routes/repl/_editor/editor.svelte b/packages/www/src/routes/repl/_editor/editor.svelte
index 7336bb97..8ad8ce4e 100644
--- a/packages/www/src/routes/repl/_editor/editor.svelte
+++ b/packages/www/src/routes/repl/_editor/editor.svelte
@@ -1,8 +1,3 @@
-<CodeMirror
-    bind:this={codemirror}
-    on:change={({ detail }) => update($file.name, detail.content)}
-/>
-
 <script>
 import { file, update, error } from "../_lib/stores.js";
 
@@ -19,3 +14,8 @@ $: if(codemirror && $error) {
     codemirror.error($error);
 }
 </script>
+
+<CodeMirror
+    bind:this={codemirror}
+    on:change={({ detail }) => update($file.name, detail.content)}
+/>
diff --git a/packages/www/src/routes/repl/_lib/hash.js b/packages/www/src/routes/repl/_lib/hash.js
index 6459d45c..ba757c9f 100644
--- a/packages/www/src/routes/repl/_lib/hash.js
+++ b/packages/www/src/routes/repl/_lib/hash.js
@@ -49,7 +49,7 @@ const decode = (hash) => {
         }
     } catch(e) {
         if(hash.length) {
-            // eslint-disable-next-line no-console
+            // eslint-disable-next-line no-console -- kaboom
             console.warn("Unable to parse hash:", e);
         }
 
diff --git a/packages/www/src/routes/repl/_lib/stores.js b/packages/www/src/routes/repl/_lib/stores.js
index e44b2ecc..156b00b5 100644
--- a/packages/www/src/routes/repl/_lib/stores.js
+++ b/packages/www/src/routes/repl/_lib/stores.js
@@ -47,7 +47,7 @@ const selected = derived([ file, files ], ([ $file, $files ]) => {
         name : $file,
         src  : $files.get($file),
     };
-});
+}, {});
 
 const output = derived([ files, error ], ([ , $error ], set) => {
     if($error) {
@@ -55,7 +55,7 @@ const output = derived([ files, error ], ([ , $error ], set) => {
     }
 
     processor.output().then(set);
-});
+}, false);
 
 const add = async (name, data = "") => {
     let idx = map.size;
@@ -100,7 +100,7 @@ const update = async (name, data = "") => {
 
         error.set(false);
     } catch(e) {
-        // eslint-disable-next-line no-console
+        // eslint-disable-next-line no-console -- error handling
         console.warn(`Error parsing "${e.file}"`, e);
 
         error.set(e);
diff --git a/packages/www/src/routes/repl/_output.svelte b/packages/www/src/routes/repl/_output.svelte
index 753b3b60..fa4cd091 100644
--- a/packages/www/src/routes/repl/_output.svelte
+++ b/packages/www/src/routes/repl/_output.svelte
@@ -1,9 +1,3 @@
-<link href="./_output.mcss" />
-
-<div class="{css.output}">
-    <CodeMirror {config} bind:this={codemirror} />
-</div>
-
 <script>
 import { onMount } from "svelte";
 
@@ -11,6 +5,8 @@ import { output } from "./_lib/stores.js";
 
 import CodeMirror from "./_editor/codemirror.svelte";
 
+import css from "./_output.mcss";
+
 const header = "/* == MODULAR-CSS OUTPUT == */";
 
 const config = {
@@ -36,3 +32,7 @@ onMount(() => {
     codemirror.input(header);
 });
 </script>
+
+<div class={css.output}>
+    <CodeMirror {config} bind:this={codemirror} />
+</div>
diff --git a/packages/www/src/routes/repl/_tabs.svelte b/packages/www/src/routes/repl/_tabs.svelte
index 2f5e23b1..73c4b4a5 100644
--- a/packages/www/src/routes/repl/_tabs.svelte
+++ b/packages/www/src/routes/repl/_tabs.svelte
@@ -1,38 +1,37 @@
-<link href="./_tabs.mcss" />
+<script>
+import { files, file, remove, add, select } from "./_lib/stores.js";
+import css from "./_tabs.mcss";
+</script>
 
-<div class="{css.tabs}">
+<div class={css.tabs}>
     <button
-        class="{css.add}"
+        class={css.add}
         title="Add a new file"
         on:click={() => add()}
     >
         ➕
     </button>
     
-    <div class="{css.tabs}">
+    <div class={css.tabs}>
         {#each [ ...$files.keys() ] as f (f)}
-        <div class="{$file.name === f ? css.tabon : css.tab}">
-            <button
-                class="{css.file}"
-                on:click={() => select(f)}
-            >
-                {f}
-            </button>
-
-            {#if $files.size > 1}
+            <div class={$file.name === f ? css.tabon : css.tab}>
                 <button
-                    class="{css.remove}"
-                    title="Remove {f}"
-                    on:click={() => remove(f)}
+                    class={css.file}
+                    on:click={() => select(f)}
                 >
-                    ❌
+                    {f}
                 </button>
-            {/if}
-        </div>
+
+                {#if $files.size > 1}
+                    <button
+                        class={css.remove}
+                        title="Remove {f}"
+                        on:click={() => remove(f)}
+                    >
+                        ❌
+                    </button>
+                {/if}
+            </div>
         {/each}
     </div>
 </div>
-
-<script>
-import { files, file, remove, add, select } from "./_lib/stores.js";
-</script>
diff --git a/packages/www/src/routes/repl/index.svelte b/packages/www/src/routes/repl/index.svelte
index d6953a73..b1991335 100644
--- a/packages/www/src/routes/repl/index.svelte
+++ b/packages/www/src/routes/repl/index.svelte
@@ -1,20 +1,3 @@
-<link href="./_repl.mcss" />
-<Title title="REPL" />
-
-<div class="{css.repl}">
-    <Tabs />
-
-    <div class="{css.input}">
-        <Editor />
-    </div>
-
-    <Output />
-    
-    <div class="{css.error}" data-visible={Boolean($error)}>
-        <pre>{$error && $error.toString()}</pre>
-    </div>
-</div>
-
 <script context="module">
 export function load() {
     return {
@@ -28,7 +11,7 @@ export function load() {
 <script>
 import { onMount } from "svelte";
 
-import Title from "$lib/title.svelte"
+import Title from "$lib/title.svelte";
 
 import Editor from "./_editor/editor.svelte";
 
@@ -38,5 +21,24 @@ import { error } from "./_lib/stores.js";
 import Tabs from "./_tabs.svelte";
 import Output from "./_output.svelte";
 
+import css from "./_repl.mcss";
+
 onMount(hash);
 </script>
+
+<Title title="REPL" />
+
+<div class={css.repl}>
+    <Tabs />
+
+    <div class={css.input}>
+        <Editor />
+    </div>
+
+    <Output />
+    
+    <div class={css.error} data-visible={Boolean($error)}>
+        <pre>{$error && $error.toString()}</pre>
+    </div>
+</div>
+


Test patch:
diff --git a/packages/browserify/test/browserify.test.js b/packages/browserify/test/browserify.test.js
index 429dabbb..34fa10f9 100644
--- a/packages/browserify/test/browserify.test.js
+++ b/packages/browserify/test/browserify.test.js
@@ -25,7 +25,7 @@ describe("/browserify.js", () => {
             expect(error).not.toHaveBeenCalled();
         });
         
-        // eslint-disable-next-line jest/no-done-callback
+        // eslint-disable-next-line jest/no-done-callback -- how this has to work
         it("should error if an invalid extension is applied", (done) => {
             const build = browserify();
             
@@ -38,7 +38,7 @@ describe("/browserify.js", () => {
             build.plugin(plugin, { ext : false });
         });
 
-        // eslint-disable-next-line jest/no-done-callback
+        // eslint-disable-next-line jest/no-done-callback -- how this has to work
         it("should error on invalid CSS", (done) => {
             const build = browserify({
                     entries : from("require('./packages/browserify/test/specimens/invalid.css');"),
@@ -54,7 +54,7 @@ describe("/browserify.js", () => {
                 expect(err).toBeTruthy();
                 
                 if(errors === 1) {
-                    // eslint-disable-next-line jest/no-conditional-expect
+                    // eslint-disable-next-line jest/no-conditional-expect -- why not?
                     expect(err.name).toMatch(/SyntaxError|CssSyntaxError/);
                     
                     return false;
diff --git a/packages/browserify/test/issue-105.test.js b/packages/browserify/test/issue-105.test.js
index d7d1da70..bd4316dc 100644
--- a/packages/browserify/test/issue-105.test.js
+++ b/packages/browserify/test/issue-105.test.js
@@ -12,8 +12,7 @@ describe("/browserify.js", () => {
         describe("/105", () => {
             // These tests can't be run until I can get onto a POSIX system and submit a symlink (ugh)
             
-            // This test in particular is probably very broken now
-            // eslint-disable-next-line jest/no-disabled-tests
+            // eslint-disable-next-line jest/no-disabled-tests -- This test in particular is probably very broken now
             it.skip("should be able to compose using a symlink", () => {
                 const processor = new Processor();
                 
@@ -21,7 +20,7 @@ describe("/browserify.js", () => {
                     .then((result) => expect(result).toMatchSnapshot());
             });
             
-            // eslint-disable-next-line jest/no-disabled-tests
+            // eslint-disable-next-line jest/no-disabled-tests -- This test in particular is probably very broken now
             it.skip("should be able to reference symlinked files when running through browserify", () => {
                 const build = browserify({
                         entries : from("require('./packages/browserify/test/specimens/issues/105/symlink.css');"),
diff --git a/packages/browserify/test/issue-313.test.js b/packages/browserify/test/issue-313.test.js
index 56c6dbc8..7d02ef18 100644
--- a/packages/browserify/test/issue-313.test.js
+++ b/packages/browserify/test/issue-313.test.js
@@ -11,7 +11,7 @@ const plugin = require("../browserify.js");
 describe("/browserify.js", () => {
     describe("/issues", () => {
         describe("/313", () => {
-            // eslint-disable-next-line jest/no-done-callback
+            // eslint-disable-next-line jest/no-done-callback -- how it has to be
             it("should include all dependencies after watchify update", (done) => {
                 const build = browserify(
                         from(dedent(`
diff --git a/packages/browserify/test/watchify.test.js b/packages/browserify/test/watchify.test.js
index 05f61947..5000c7f4 100644
--- a/packages/browserify/test/watchify.test.js
+++ b/packages/browserify/test/watchify.test.js
@@ -14,7 +14,7 @@ describe("/browserify.js", () => {
         afterAll(() => shell.rm("-rf", "./packages/browserify/test/output/watchify"));
         
         // NOTE: Other watchify tests are in issue files
-        // eslint-disable-next-line jest/no-done-callback
+        // eslint-disable-next-line jest/no-done-callback -- browserify lol
         it("shouldn't cache file contents between watchify runs", (done) => {
             const build = browserify();
             
@@ -54,7 +54,7 @@ describe("/browserify.js", () => {
                 });
         });
 
-        // eslint-disable-next-line jest/no-done-callback
+        // eslint-disable-next-line jest/no-done-callback -- browserify lol
         it("shouldn't explode on invalid CSS", (done) => {
             const build = browserify();
             let wait;
diff --git a/packages/css-to-js/test/api.test.js b/packages/css-to-js/test/api.test.js
index c42ce123..91e55712 100644
--- a/packages/css-to-js/test/api.test.js
+++ b/packages/css-to-js/test/api.test.js
@@ -1,4 +1,3 @@
-/* eslint max-statements: "off" */
 "use strict";
 
 const path = require("path");
diff --git a/packages/rollup/test/rollup.test.js b/packages/rollup/test/rollup.test.js
index 8340df00..3c1938f1 100644
--- a/packages/rollup/test/rollup.test.js
+++ b/packages/rollup/test/rollup.test.js
@@ -1,4 +1,3 @@
-/* eslint max-statements: "off" */
 "use strict";
 
 const { rollup } = require("rollup");
@@ -27,6 +26,7 @@ const assetFileNames = "assets/[name][extname]";
 const format = "es";
 const map = false;
 
+// eslint-disable-next-line max-statements -- I got a lotta tests c'mon
 describe("/rollup.js", () => {
     const createPlugin = (opts = {}) => plugin({
         namer,
@@ -838,10 +838,10 @@ describe("/rollup.js", () => {
                 file : prefix(`./output/casing/main.js`),
             });
 
-            // eslint-disable-next-line jest/no-standalone-expect
+            // eslint-disable-next-line jest/no-standalone-expect -- we're getting fancy
             expect(spy).toHaveBeenCalled();
 
-            // eslint-disable-next-line jest/no-standalone-expect
+            // eslint-disable-next-line jest/no-standalone-expect -- we be fancy
             expect(spy.mock.calls.length).toBeGreaterThan(0);
         });
     });
@@ -881,7 +881,7 @@ describe("/rollup.js", () => {
             .finally(() => expect(e.toString()).toMatch(".wooga"));
         });
 
-        // eslint-disable-next-line jest/expect-expect
+        // eslint-disable-next-line jest/expect-expect -- just looking for the throw
         it("should throw errors in in before plugins", () =>
             rollup({
                 input   : require.resolve("./specimens/simple.js"),
@@ -895,7 +895,7 @@ describe("/rollup.js", () => {
             .catch(checkError)
         );
 
-        // eslint-disable-next-line jest/expect-expect
+        // eslint-disable-next-line jest/expect-expect -- just looking for the throw
         it("should throw errors in after plugins", () =>
             rollup({
                 input   : require.resolve("./specimens/simple.js"),
@@ -910,7 +910,7 @@ describe("/rollup.js", () => {
         );
 
         // Skipped because I can't figure out how to catch the error being thrown?
-        // eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect
+        // eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect -- shit happens
         it.skip("should throw errors in done plugins", () =>
             rollup({
                 input   : require.resolve("./specimens/simple.js"),
diff --git a/packages/rollup/test/splitting.test.js b/packages/rollup/test/splitting.test.js
index c164f7a8..8551fc67 100644
--- a/packages/rollup/test/splitting.test.js
+++ b/packages/rollup/test/splitting.test.js
@@ -1,4 +1,3 @@
-/* eslint max-statements: "off" */
 "use strict";
 
 const { rollup } = require("rollup");
diff --git a/packages/rollup/test/watch.test.js b/packages/rollup/test/watch.test.js
index 2d013866..71fa9ecb 100644
--- a/packages/rollup/test/watch.test.js
+++ b/packages/rollup/test/watch.test.js
@@ -1,4 +1,3 @@
-/* eslint consistent-return: off */
 "use strict";
 
 const shell = require("shelljs");
diff --git a/packages/svelte/test/__snapshots__/svelte.test.js.snap b/packages/svelte/test/__snapshots__/svelte.test.js.snap
index 0d8c635f..837de96f 100644
--- a/packages/svelte/test/__snapshots__/svelte.test.js.snap
+++ b/packages/svelte/test/__snapshots__/svelte.test.js.snap
@@ -704,6 +704,27 @@ exports[`/svelte.js should no-op if all <link>s reference a URL 1`] = `
 "
 `;
 
+exports[`/svelte.js should replace unquoted class attributes correctly 1`] = `
+"<div class=\\"mc_a mc_b\\">
+    I'm not quoted lol
+    <div class=\\"mc_a\\">Me either!</div>
+</div>
+
+<div class=\\"mc_a mc_b\\">
+    But I am!
+</div>
+
+"
+`;
+
+exports[`/svelte.js should replace unquoted class attributes correctly 2`] = `
+"/* packages/svelte/test/specimens/unquoted.svelte */
+.mc_a {
+        color: red;
+    }
+"
+`;
+
 exports[`/svelte.js should support verbose output: <link> 1`] = `
 Array [
   Array [
@@ -1072,23 +1093,6 @@ Array [
 ]
 `;
 
-exports[`/svelte.js should warn about unquoted class attributes 1`] = `
-Array [
-  Array [
-    "[@modular-css/svelte]",
-    "WARN",
-    "Unquoted class attribute! class={css.b}",
-    "packages/svelte/test/specimens/unquoted.svelte",
-  ],
-  Array [
-    "[@modular-css/svelte]",
-    "WARN",
-    "Unquoted class attribute! class={css.a}",
-    "packages/svelte/test/specimens/unquoted.svelte",
-  ],
-]
-`;
-
 exports[`/svelte.js should warn when multiple <link> elements are in the html 1`] = `
 "<link rel=\\"stylesheet\\" href=\\"./simple2.css\\" />
 
diff --git a/packages/svelte/test/svelte-rollup.test.js b/packages/svelte/test/svelte-rollup.test.js
index a42732c8..19b42cdf 100644
--- a/packages/svelte/test/svelte-rollup.test.js
+++ b/packages/svelte/test/svelte-rollup.test.js
@@ -1,4 +1,3 @@
-/* eslint consistent-return: off */
 "use strict";
 
 const path = require("path");
diff --git a/packages/svelte/test/svelte.test.js b/packages/svelte/test/svelte.test.js
index 4b97d672..ef5285de 100644
--- a/packages/svelte/test/svelte.test.js
+++ b/packages/svelte/test/svelte.test.js
@@ -45,6 +45,28 @@ describe("/svelte.js", () => {
         expect(output.css).toMatchSnapshot();
     });
 
+    it("should replace unquoted class attributes correctly", async () => {
+        const filename = require.resolve(`./specimens/unquoted.svelte`);
+        const { processor, preprocess } = plugin({
+            namer,
+            values : true,
+        });
+
+        const processed = await svelte.preprocess(
+            fs.readFileSync(filename, "utf8"),
+            {
+                ...preprocess,
+                filename,
+            },
+        );
+
+        expect(processed.toString()).toMatchSnapshot();
+
+        const output = await processor.output();
+
+        expect(output.css).toMatchSnapshot();
+    });
+
     it("should ignore <style> tags without the text/m-css attribute", async () => {
         const filename = require.resolve("./specimens/style-no-attribute.svelte");
         const { processor, preprocess } = plugin({
@@ -453,26 +475,6 @@ describe("/svelte.js", () => {
         expect(output.css).toMatchSnapshot();
     });
 
-    it("should warn about unquoted class attributes", async () => {
-        const spy = logspy("warn");
-
-        const filename = require.resolve("./specimens/unquoted.svelte");
-        const { preprocess } = plugin({
-            namer,
-        });
-
-        await svelte.preprocess(
-            fs.readFileSync(filename, "utf8"),
-            {
-                ...preprocess,
-                filename,
-            },
-        );
-
-        expect(spy).toHaveBeenCalled();
-        expect(spy).toMatchLogspySnapshot();
-    });
-
     it("should ignore <Link />", async () => {
         const spy = logspy("warn");
 
diff --git a/packages/test-utils/expect/toMatchLogspySnapshot.js b/packages/test-utils/expect/toMatchLogspySnapshot.js
index c93ea5b4..0e33ae82 100644
--- a/packages/test-utils/expect/toMatchLogspySnapshot.js
+++ b/packages/test-utils/expect/toMatchLogspySnapshot.js
@@ -9,7 +9,7 @@ const relative = require("../relative.js");
 expect.extend({
     toMatchLogspySnapshot(spy) {
         if(!spy || !spy.mock) {
-            // eslint-disable-next-line no-console
+            // eslint-disable-next-line no-console -- booooooom
             console.log({ spy, "spy.mock" : spy.mock });
 
             return {
diff --git a/packages/test-utils/rollup-watching.js b/packages/test-utils/rollup-watching.js
index 8aa69f64..c857b175 100644
--- a/packages/test-utils/rollup-watching.js
+++ b/packages/test-utils/rollup-watching.js
@@ -29,7 +29,7 @@ module.exports.promise = (watcher) => {
         }
         
         if(e.code !== "END") {
-            // eslint-disable-next-line consistent-return
+            // eslint-disable-next-line consistent-return -- shit happens
             return;
         }
 
diff --git a/packages/webpack/test/webpack.test.js b/packages/webpack/test/webpack.test.js
index 52202412..5b8bbe68 100644
--- a/packages/webpack/test/webpack.test.js
+++ b/packages/webpack/test/webpack.test.js
@@ -272,11 +272,11 @@ describe("/webpack.js", () => {
     }));
 
     // TODO: How on earth do I get webpack to tell me what files changed?
-    // eslint-disable-next-line jest/no-disabled-tests
+    // eslint-disable-next-line jest/no-disabled-tests -- I dunno
     it.skip("should generate correct builds when files change", () => {
         const changed = "./packages/webpack/test/output/changed.css";
 
-        // eslint-disable-next-line prefer-const
+        // eslint-disable-next-line prefer-const -- need to be able to write to it
         let compiler;
 
         // wrap compiler.run in a promise for easier chaining
@@ -317,7 +317,7 @@ describe("/webpack.js", () => {
             })
             // This build fails because the file is missing
             .catch((stats) => {
-                // eslint-disable-next-line jest/no-conditional-expect
+                // eslint-disable-next-line jest/no-conditional-expect -- just deal jest
                 expect(stats.toJson().errors[0].message).toMatch("Error: Can't resolve '../output/changed.css'");
 
                 fs.writeFileSync(changed, ".three { color: green; }");