@tailwind-ts/eslint-plugin 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +97 -0
  2. package/package.json +1 -21
package/README.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ESLint plugin for detecting invalid Tailwind CSS v4 class names — typos, undefined utilities, and bad `@apply` usage — in JavaScript, TypeScript, CSS, and Vue projects.
4
4
 
5
+ ## What's new in v0.2.0
6
+
7
+ - **`cva()` variant validation** — lints base classes and variant values inside `class-variance-authority` configs
8
+ - **CSS `@apply` linting** — new `no-invalid-apply` rule for `.css` files
9
+ - **Typo suggestions** — reports `Did you mean "flex"?` when a close match exists
10
+ - **Import-aware helpers** — validates `cn()` / `clsx()` when imported from `@/lib/utils`
11
+ - **Vue `:class` support** — lints class bindings in `.vue` files
12
+ - **`tv()` helper** — tailwind-variants added to defaults
13
+ - **Config presets** — `react`, `vue`, and `css` presets
14
+ - **Multi-entry CSS** — `cssConfigPath` accepts a string or array for monorepos
15
+ - **Batch validation** — faster lint runs via batched worker calls and shared cache
16
+
5
17
  ## Features
6
18
 
7
19
  - **`cva()` variant validation** — lints base classes and variant values inside `class-variance-authority` configs
@@ -84,6 +96,19 @@ export default [
84
96
  | `vue` | `*.{vue,js,ts}` | Enables `:class` parsing (requires `vue-eslint-parser`) |
85
97
  | `css` | `**/*.css` | Enables `@apply` validation |
86
98
 
99
+ Combine presets for full-stack apps:
100
+
101
+ ```js
102
+ export default [
103
+ {
104
+ extends: [tailwindTs.configs.react, tailwindTs.configs.css],
105
+ settings: {
106
+ tailwindTs: { cssConfigPath: "./src/styles/tailwind.css" },
107
+ },
108
+ },
109
+ ];
110
+ ```
111
+
87
112
  ## Rules
88
113
 
89
114
  | Rule | Description | Default |
@@ -91,6 +116,61 @@ export default [
91
116
  | [`no-invalid-classname`](./docs/rules/no-invalid-classname.md) | Disallow invalid Tailwind classes in JSX, helpers, and Vue bindings | `error` |
92
117
  | [`no-invalid-apply`](./docs/rules/no-invalid-apply.md) | Disallow invalid classes in CSS `@apply` directives | `error` |
93
118
 
119
+ Both rules share the same Tailwind design-system validator and support typo suggestions.
120
+
121
+ ## Examples
122
+
123
+ ### JSX typo with suggestion
124
+
125
+ ```jsx
126
+ // error: Class "flexs" is not a valid Tailwind CSS class. Did you mean "flex"?
127
+ <div className="flexs items-center" />
128
+ ```
129
+
130
+ ### `cva()` variant values
131
+
132
+ ```ts
133
+ import { cva } from "class-variance-authority";
134
+
135
+ const button = cva("rounded-lg", {
136
+ variants: {
137
+ size: {
138
+ sm: "p-2 text-lgg", // error on "text-lgg"
139
+ md: "p-4 text-base",
140
+ },
141
+ },
142
+ });
143
+ ```
144
+
145
+ ### CSS `@apply`
146
+
147
+ ```css
148
+ .card {
149
+ @apply flexs items-center rounded-lg; /* error */
150
+ }
151
+ ```
152
+
153
+ ### Import-aware `cn()`
154
+
155
+ ```tsx
156
+ import { cn } from "@/lib/utils";
157
+
158
+ <div className={cn("p-4", "text-lgg")} />
159
+ ```
160
+
161
+ ### Monorepo CSS entries
162
+
163
+ ```js
164
+ settings: {
165
+ tailwindTs: {
166
+ cssConfigPath: [
167
+ "./apps/web/src/styles/tailwind.css",
168
+ "./apps/admin/src/styles/tailwind.css",
169
+ ],
170
+ },
171
+ },
172
+ ```
173
+
94
174
  ## Settings
95
175
 
96
176
  | Option | Type | Default | Description |
@@ -99,7 +179,10 @@ export default [
99
179
  | `attributes` | `string[]` | `["class", "className"]` | JSX/Vue attributes to lint |
100
180
  | `functions` | `string[]` | `["cn", "clsx", "cva", "tv", "twMerge", "twJoin"]` | Helper calls to lint |
101
181
  | `whitelist` | `string[]` | `[]` | Regex patterns for allowed non-Tailwind classes |
182
+ | `ignoredKeys` | `string[]` | `["defaultVariants", "compoundVariants", "compoundSlots"]` | Object keys skipped in helper calls |
102
183
  | `suggestSimilarClasses` | `boolean` | `true` | Include "Did you mean …?" hints for typos |
184
+ | `cacheMaxAge` | `number` | `600000` | Validation cache TTL in ms (10 min) |
185
+ | `cacheMaxSize` | `number` | `250000` | Max cached validation entries |
103
186
 
104
187
  ### Typesafe settings
105
188
 
@@ -109,6 +192,7 @@ settings: {
109
192
  /** @type {import('@tailwind-ts/eslint-plugin').PluginSettings} */
110
193
  ({
111
194
  cssConfigPath: "./src/styles/tailwind.css",
195
+ suggestSimilarClasses: true,
112
196
  }),
113
197
  },
114
198
  ```
@@ -116,8 +200,21 @@ settings: {
116
200
  ## Limitations
117
201
 
118
202
  - Dynamic template interpolation (e.g. `` className={`p-${size}`} ``) is not validated
203
+ - Spread props (`className={props.className}`) are skipped unless the value is a static literal upstream
119
204
  - Vue support requires `vue-eslint-parser` in your ESLint config
120
205
 
206
+ ## Migration from v0.1.0
207
+
208
+ No breaking changes to rule names or settings. v0.2.0 adds optional presets and the `no-invalid-apply` rule via `configs.css`.
209
+
210
+ ```js
211
+ // v0.1.0 — still works
212
+ extends: [tailwindTs.configs.recommended]
213
+
214
+ // v0.2.0 — add CSS @apply linting
215
+ extends: [tailwindTs.configs.recommended, tailwindTs.configs.css]
216
+ ```
217
+
121
218
  ## License
122
219
 
123
220
  MIT
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "@tailwind-ts/eslint-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "ESLint plugin for detecting invalid Tailwind CSS v4 class names with cva, @apply, and typo suggestions",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "author": "",
8
7
  "repository": {
9
8
  "type": "git",
10
9
  "url": "git+https://github.com/tailwind-ts/eslint-plugin.git"
@@ -47,12 +46,6 @@
47
46
  "docs/",
48
47
  "lib/"
49
48
  ],
50
- "scripts": {
51
- "build": "unbuild --config unbuild.config.ts && node scripts/copy-worker.mjs",
52
- "test": "vitest run",
53
- "lint": "eslint .",
54
- "prepublishOnly": "npm run build && npm test"
55
- },
56
49
  "dependencies": {
57
50
  "@typescript-eslint/utils": "^8.37.0",
58
51
  "synckit": "^0.11.11"
@@ -69,18 +62,5 @@
69
62
  "vue-eslint-parser": {
70
63
  "optional": true
71
64
  }
72
- },
73
- "devDependencies": {
74
- "@eslint/js": "^9.31.0",
75
- "@tailwindcss/node": "^4.3.2",
76
- "@types/node": "^22.15.0",
77
- "@typescript-eslint/parser": "^8.37.0",
78
- "@typescript-eslint/rule-tester": "^8.37.0",
79
- "eslint": "^9.31.0",
80
- "tailwindcss": "^4.3.2",
81
- "typescript": "^5.9.2",
82
- "typescript-eslint": "^8.42.0",
83
- "unbuild": "^3.3.1",
84
- "vitest": "^3.2.4"
85
65
  }
86
66
  }