tailwind-typescript-plugin 1.4.0-beta.2 → 1.4.0-beta.20
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.
- package/CHANGELOG.md +129 -0
- package/README.md +403 -77
- package/lib/core/interfaces.d.ts +23 -12
- package/lib/core/interfaces.d.ts.map +1 -1
- package/lib/core/types.d.ts +112 -1
- package/lib/core/types.d.ts.map +1 -1
- package/lib/extractors/BaseExtractor.d.ts +49 -2
- package/lib/extractors/BaseExtractor.d.ts.map +1 -1
- package/lib/extractors/BaseExtractor.js +184 -5
- package/lib/extractors/BaseExtractor.js.map +1 -1
- package/lib/extractors/CvaExtractor.js +1 -1
- package/lib/extractors/CvaExtractor.js.map +1 -1
- package/lib/extractors/ExpressionExtractor.d.ts.map +1 -1
- package/lib/extractors/ExpressionExtractor.js +35 -5
- package/lib/extractors/ExpressionExtractor.js.map +1 -1
- package/lib/extractors/JsxAttributeExtractor.d.ts +2 -1
- package/lib/extractors/JsxAttributeExtractor.d.ts.map +1 -1
- package/lib/extractors/JsxAttributeExtractor.js +15 -8
- package/lib/extractors/JsxAttributeExtractor.js.map +1 -1
- package/lib/extractors/SvelteAttributeExtractor.d.ts +17 -0
- package/lib/extractors/SvelteAttributeExtractor.d.ts.map +1 -0
- package/lib/extractors/SvelteAttributeExtractor.js +27 -0
- package/lib/extractors/SvelteAttributeExtractor.js.map +1 -0
- package/lib/extractors/TailwindVariantsExtractor.d.ts.map +1 -1
- package/lib/extractors/TailwindVariantsExtractor.js +1 -5
- package/lib/extractors/TailwindVariantsExtractor.js.map +1 -1
- package/lib/extractors/VariableReferenceExtractor.d.ts.map +1 -1
- package/lib/extractors/VariableReferenceExtractor.js +21 -0
- package/lib/extractors/VariableReferenceExtractor.js.map +1 -1
- package/lib/extractors/VueAttributeExtractor.d.ts +119 -0
- package/lib/extractors/VueAttributeExtractor.d.ts.map +1 -0
- package/lib/extractors/VueAttributeExtractor.js +911 -0
- package/lib/extractors/VueAttributeExtractor.js.map +1 -0
- package/lib/extractors/VueExpressionExtractor.d.ts +21 -0
- package/lib/extractors/VueExpressionExtractor.d.ts.map +1 -0
- package/lib/extractors/VueExpressionExtractor.js +51 -0
- package/lib/extractors/VueExpressionExtractor.js.map +1 -0
- package/lib/infrastructure/TailwindConflictDetector.d.ts +28 -18
- package/lib/infrastructure/TailwindConflictDetector.d.ts.map +1 -1
- package/lib/infrastructure/TailwindConflictDetector.js +216 -486
- package/lib/infrastructure/TailwindConflictDetector.js.map +1 -1
- package/lib/infrastructure/TailwindValidator.css-vars.spec.js +1 -11
- package/lib/infrastructure/TailwindValidator.css-vars.spec.js.map +1 -1
- package/lib/infrastructure/TailwindValidator.d.ts +16 -3
- package/lib/infrastructure/TailwindValidator.d.ts.map +1 -1
- package/lib/infrastructure/TailwindValidator.js +83 -24
- package/lib/infrastructure/TailwindValidator.js.map +1 -1
- package/lib/infrastructure/TailwindValidator.spec.js +196 -12
- package/lib/infrastructure/TailwindValidator.spec.js.map +1 -1
- package/lib/plugin/TailwindTypescriptPlugin.d.ts +30 -1
- package/lib/plugin/TailwindTypescriptPlugin.d.ts.map +1 -1
- package/lib/plugin/TailwindTypescriptPlugin.js +209 -35
- package/lib/plugin/TailwindTypescriptPlugin.js.map +1 -1
- package/lib/services/ClassNameExtractionService.d.ts +21 -5
- package/lib/services/ClassNameExtractionService.d.ts.map +1 -1
- package/lib/services/ClassNameExtractionService.js +76 -14
- package/lib/services/ClassNameExtractionService.js.map +1 -1
- package/lib/services/ClassNameExtractionService.spec.d.ts +2 -0
- package/lib/services/ClassNameExtractionService.spec.d.ts.map +1 -0
- package/lib/services/ClassNameExtractionService.spec.js +222 -0
- package/lib/services/ClassNameExtractionService.spec.js.map +1 -0
- package/lib/services/CodeActionService.spec.js +1 -2
- package/lib/services/CodeActionService.spec.js.map +1 -1
- package/lib/services/CompletionService.d.ts +121 -0
- package/lib/services/CompletionService.d.ts.map +1 -0
- package/lib/services/CompletionService.js +573 -0
- package/lib/services/CompletionService.js.map +1 -0
- package/lib/services/CompletionService.spec.d.ts +2 -0
- package/lib/services/CompletionService.spec.d.ts.map +1 -0
- package/lib/services/CompletionService.spec.js +1182 -0
- package/lib/services/CompletionService.spec.js.map +1 -0
- package/lib/services/ConflictClassDetection.spec.js +26 -21
- package/lib/services/ConflictClassDetection.spec.js.map +1 -1
- package/lib/services/DiagnosticService.d.ts +8 -8
- package/lib/services/DiagnosticService.d.ts.map +1 -1
- package/lib/services/DiagnosticService.js +29 -13
- package/lib/services/DiagnosticService.js.map +1 -1
- package/lib/services/DuplicateClassDetection.spec.js +61 -81
- package/lib/services/DuplicateClassDetection.spec.js.map +1 -1
- package/lib/services/PluginConfigService.d.ts +47 -15
- package/lib/services/PluginConfigService.d.ts.map +1 -1
- package/lib/services/PluginConfigService.js +203 -75
- package/lib/services/PluginConfigService.js.map +1 -1
- package/lib/services/PluginConfigService.spec.d.ts +2 -0
- package/lib/services/PluginConfigService.spec.d.ts.map +1 -0
- package/lib/services/PluginConfigService.spec.js +93 -0
- package/lib/services/PluginConfigService.spec.js.map +1 -0
- package/lib/services/ValidationService.d.ts +8 -5
- package/lib/services/ValidationService.d.ts.map +1 -1
- package/lib/services/ValidationService.js +49 -49
- package/lib/services/ValidationService.js.map +1 -1
- package/lib/utils/FrameworkDetector.d.ts +24 -0
- package/lib/utils/FrameworkDetector.d.ts.map +1 -0
- package/lib/utils/FrameworkDetector.js +52 -0
- package/lib/utils/FrameworkDetector.js.map +1 -0
- package/lib/utils/FrameworkDetector.spec.d.ts +2 -0
- package/lib/utils/FrameworkDetector.spec.d.ts.map +1 -0
- package/lib/utils/FrameworkDetector.spec.js +75 -0
- package/lib/utils/FrameworkDetector.spec.js.map +1 -0
- package/package.json +4 -2
- package/lib/extractors/StringLiteralExtractor.d.ts +0 -12
- package/lib/extractors/StringLiteralExtractor.d.ts.map +0 -1
- package/lib/extractors/StringLiteralExtractor.js +0 -21
- package/lib/extractors/StringLiteralExtractor.js.map +0 -1
- package/lib/services/ClassNameExtractionService.original.d.ts +0 -20
- package/lib/services/ClassNameExtractionService.original.d.ts.map +0 -1
- package/lib/services/ClassNameExtractionService.original.js +0 -48
- package/lib/services/ClassNameExtractionService.original.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
[](https://github.com/IvanRodriCalleja/tailwind-typescript-plugin/blob/main/CONTRIBUTING.md)
|
|
8
8
|
|
|
9
|
-
A TypeScript Language Service plugin that catches **typos and invalid Tailwind CSS class names** in your JSX/TSX files. When you write a class name that doesn't exist in Tailwind, it won't apply any styles—this plugin detects those mistakes and shows errors directly in your editor before you ship broken styles.
|
|
9
|
+
A TypeScript Language Service plugin that catches **typos and invalid Tailwind CSS class names** in your JSX/TSX and Vue files. When you write a class name that doesn't exist in Tailwind, it won't apply any styles—this plugin detects those mistakes and shows errors directly in your editor before you ship broken styles.
|
|
10
10
|
|
|
11
11
|
## What does this plugin do?
|
|
12
12
|
|
|
13
|
-
Ever written `className="flex itms-center"` instead of `"flex items-center"`? That typo silently fails—Tailwind ignores invalid classes and your component looks broken. This plugin prevents that by analyzing your JSX/TSX code
|
|
13
|
+
Ever written `className="flex itms-center"` instead of `"flex items-center"`? That typo silently fails—Tailwind ignores invalid classes and your component looks broken. This plugin prevents that by analyzing your JSX/TSX and Vue code, validating that all Tailwind classes used in `className`/`class` attributes actually exist in your Tailwind CSS configuration. It provides real-time feedback by showing TypeScript errors for invalid or misspelled Tailwind classes, catching styling mistakes before they reach production.
|
|
14
14
|
|
|
15
15
|
## Table of Contents
|
|
16
16
|
|
|
@@ -37,6 +37,10 @@ Ever written `className="flex itms-center"` instead of `"flex items-center"`? Th
|
|
|
37
37
|
- **Editor integration**: Works with any editor that supports TypeScript Language Service (VS Code, WebStorm, etc.)
|
|
38
38
|
- **Supports Tailwind variants**: Validates responsive (`md:`, `lg:`), state (`hover:`, `focus:`), and other variants
|
|
39
39
|
- **Arbitrary values**: Correctly handles Tailwind arbitrary values like `h-[50vh]` or `bg-[#ff0000]`
|
|
40
|
+
- **Vue support**: Validates classes in Vue Single File Components (`.vue` files) when used with `@vue/typescript-plugin`
|
|
41
|
+
- Static class attributes: `<div class="flex items-center">`
|
|
42
|
+
- Dynamic class bindings: `<div :class="{ 'bg-red-500': isActive }">`
|
|
43
|
+
- Object and array syntax support
|
|
40
44
|
- **Variant library support**:
|
|
41
45
|
- **tailwind-variants**: Validates classes in `tv()` function calls including `base`, `variants`, `compoundVariants`, `slots`, and `class`/`className` override properties
|
|
42
46
|
- **class-variance-authority**: Validates classes in `cva()` function calls including base classes, `variants`, `compoundVariants`, and `class`/`className` override properties
|
|
@@ -59,6 +63,21 @@ pnpm add tailwind-typescript-plugin
|
|
|
59
63
|
|
|
60
64
|
Add the plugin to the `compilerOptions.plugins` array in your `tsconfig.json`:
|
|
61
65
|
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"compilerOptions": {
|
|
69
|
+
"plugins": [
|
|
70
|
+
{
|
|
71
|
+
"name": "tailwind-typescript-plugin",
|
|
72
|
+
"globalCss": "./src/global.css"
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Full configuration example with all options:**
|
|
80
|
+
|
|
62
81
|
```json
|
|
63
82
|
{
|
|
64
83
|
"compilerOptions": {
|
|
@@ -66,11 +85,44 @@ Add the plugin to the `compilerOptions.plugins` array in your `tsconfig.json`:
|
|
|
66
85
|
{
|
|
67
86
|
"name": "tailwind-typescript-plugin",
|
|
68
87
|
"globalCss": "./src/global.css",
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
"libraries": {
|
|
89
|
+
"utilities": {
|
|
90
|
+
"cn": "@/lib/utils",
|
|
91
|
+
"merge": "tailwind-merge",
|
|
92
|
+
"myFn": "*"
|
|
93
|
+
},
|
|
94
|
+
"variants": {
|
|
95
|
+
"tailwindVariants": true,
|
|
96
|
+
"classVarianceAuthority": true
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"validation": {
|
|
100
|
+
"enabled": true,
|
|
101
|
+
"severity": "error",
|
|
102
|
+
"allowedClasses": ["custom-*", "app-*"]
|
|
103
|
+
},
|
|
104
|
+
"lint": {
|
|
105
|
+
"enabled": true,
|
|
106
|
+
"conflictingClasses": {
|
|
107
|
+
"enabled": true,
|
|
108
|
+
"severity": "warning"
|
|
109
|
+
},
|
|
110
|
+
"repeatedClasses": {
|
|
111
|
+
"enabled": true,
|
|
112
|
+
"severity": "warning"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"editor": {
|
|
116
|
+
"enabled": true,
|
|
117
|
+
"autocomplete": {
|
|
118
|
+
"enabled": true
|
|
119
|
+
},
|
|
120
|
+
"hover": {
|
|
121
|
+
"enabled": true
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"classAttributes": {
|
|
125
|
+
"attributes": ["colorStyles", "textStyles"]
|
|
74
126
|
}
|
|
75
127
|
}
|
|
76
128
|
]
|
|
@@ -80,82 +132,218 @@ Add the plugin to the `compilerOptions.plugins` array in your `tsconfig.json`:
|
|
|
80
132
|
|
|
81
133
|
**Configuration options:**
|
|
82
134
|
|
|
83
|
-
|
|
135
|
+
#### `globalCss` (required)
|
|
136
|
+
Path to your global CSS file that imports Tailwind CSS. This can be relative to your project root.
|
|
84
137
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
#### `libraries` (optional)
|
|
141
|
+
Configure utility functions and variant libraries.
|
|
142
|
+
|
|
143
|
+
##### `libraries.utilities`
|
|
144
|
+
Configure which utility functions to scan for class names. Keys are function names, values control import matching:
|
|
145
|
+
|
|
146
|
+
| Value | Description |
|
|
147
|
+
|-------|-------------|
|
|
148
|
+
| `"*"` | Match any import source |
|
|
149
|
+
| `"off"` | Disable this utility function |
|
|
150
|
+
| `"package-name"` | Only match if imported from this package |
|
|
151
|
+
|
|
152
|
+
**Defaults** (automatically included):
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"cn": "*",
|
|
156
|
+
"clsx": "clsx",
|
|
157
|
+
"classnames": "classnames",
|
|
158
|
+
"classNames": "classnames",
|
|
159
|
+
"cx": "classnames",
|
|
160
|
+
"twMerge": "tailwind-merge"
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Example - Add custom utility and disable a default**:
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"libraries": {
|
|
168
|
+
"utilities": {
|
|
169
|
+
"myMerge": "@/lib/utils",
|
|
170
|
+
"clsx": "off"
|
|
107
171
|
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
108
175
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
176
|
+
**Supported import patterns**:
|
|
177
|
+
```typescript
|
|
178
|
+
// Named import
|
|
179
|
+
import { merge } from '@/lib/utils';
|
|
180
|
+
className={merge('flex', 'items-center')} // ✅ Validated
|
|
181
|
+
|
|
182
|
+
// Default import
|
|
183
|
+
import merge from '@/lib/utils';
|
|
184
|
+
className={merge('flex', 'items-center')} // ✅ Validated
|
|
185
|
+
|
|
186
|
+
// Namespace import
|
|
187
|
+
import * as utils from '@/lib/utils';
|
|
188
|
+
className={utils.merge('flex', 'items-center')} // ✅ Validated
|
|
189
|
+
|
|
190
|
+
// Aliased import
|
|
191
|
+
import { something as merge } from '@/lib/utils';
|
|
192
|
+
className={merge('flex', 'items-center')} // ✅ Validated
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
##### `libraries.variants`
|
|
196
|
+
Configure which variant library extractors to enable.
|
|
197
|
+
|
|
198
|
+
| Option | Default | Description |
|
|
199
|
+
|--------|---------|-------------|
|
|
200
|
+
| `tailwindVariants` | `true` | Enable `tv()` function support |
|
|
201
|
+
| `classVarianceAuthority` | `true` | Enable `cva()` function support |
|
|
202
|
+
|
|
203
|
+
**Example - Enable only tailwind-variants**:
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"libraries": {
|
|
207
|
+
"variants": {
|
|
208
|
+
"tailwindVariants": true,
|
|
209
|
+
"classVarianceAuthority": false
|
|
114
210
|
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
115
214
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
#### `validation` (optional)
|
|
218
|
+
Configure invalid class detection.
|
|
219
|
+
|
|
220
|
+
| Option | Default | Description |
|
|
221
|
+
|--------|---------|-------------|
|
|
222
|
+
| `enabled` | `true` | Enable/disable invalid class validation |
|
|
223
|
+
| `severity` | `"error"` | `"error"` \| `"warning"` \| `"suggestion"` \| `"off"` |
|
|
224
|
+
| `allowedClasses` | `[]` | Custom class patterns to allow |
|
|
225
|
+
|
|
226
|
+
##### `validation.allowedClasses`
|
|
227
|
+
Array of custom class names or wildcard patterns that should be treated as valid.
|
|
228
|
+
|
|
229
|
+
| Pattern | Description | Example | Matches |
|
|
230
|
+
|---------|-------------|---------|---------|
|
|
231
|
+
| `prefix-*` | Matches classes starting with prefix | `custom-*` | `custom-button`, `custom-card` |
|
|
232
|
+
| `*-suffix` | Matches classes ending with suffix | `*-icon` | `arrow-icon`, `close-icon` |
|
|
233
|
+
| `*-contains-*` | Matches classes containing the string | `*-component-*` | `app-component-header` |
|
|
234
|
+
| `exact` | Exact match (no wildcards) | `my-class` | Only `my-class` |
|
|
235
|
+
|
|
236
|
+
**Example**:
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"validation": {
|
|
240
|
+
"allowedClasses": ["custom-*", "*-icon", "exact-class"]
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
#### `lint` (optional)
|
|
248
|
+
Configure lint rules for code quality issues.
|
|
249
|
+
|
|
250
|
+
| Option | Default | Description |
|
|
251
|
+
|--------|---------|-------------|
|
|
252
|
+
| `enabled` | `true` | Master switch for all lint rules |
|
|
253
|
+
| `conflictingClasses` | `{ enabled: true, severity: "warning" }` | Detect conflicting utilities |
|
|
254
|
+
| `repeatedClasses` | `{ enabled: true, severity: "warning" }` | Detect duplicate classes |
|
|
255
|
+
|
|
256
|
+
**Example - Disable conflicting class detection**:
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"lint": {
|
|
260
|
+
"conflictingClasses": {
|
|
261
|
+
"enabled": false
|
|
122
262
|
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
123
266
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
267
|
+
**Example - Make duplicate classes an error**:
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"lint": {
|
|
271
|
+
"repeatedClasses": {
|
|
272
|
+
"severity": "error"
|
|
127
273
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
#### `editor` (optional)
|
|
281
|
+
Configure editor features like autocomplete and hover.
|
|
282
|
+
|
|
283
|
+
| Option | Default | Description |
|
|
284
|
+
|--------|---------|-------------|
|
|
285
|
+
| `enabled` | `true` | Master switch for all editor features |
|
|
286
|
+
| `autocomplete.enabled` | `true` | Enable Tailwind class autocomplete |
|
|
287
|
+
| `hover.enabled` | `true` | Enable hover information showing CSS |
|
|
288
|
+
|
|
289
|
+
**Example - Disable autocomplete**:
|
|
290
|
+
```json
|
|
291
|
+
{
|
|
292
|
+
"editor": {
|
|
293
|
+
"autocomplete": {
|
|
294
|
+
"enabled": false
|
|
138
295
|
}
|
|
139
|
-
|
|
140
|
-
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
#### `classAttributes` (optional)
|
|
303
|
+
Configure which JSX/HTML attributes should be treated as class attributes.
|
|
304
|
+
|
|
305
|
+
By default, the plugin validates classes in `className`, `class`, and `classList` attributes. You can add additional attributes for frameworks like React Native that use different prop names for styling.
|
|
306
|
+
|
|
307
|
+
| Option | Default | Description |
|
|
308
|
+
|--------|---------|-------------|
|
|
309
|
+
| `attributes` | `[]` | Additional attribute names to treat as class attributes |
|
|
310
|
+
|
|
311
|
+
**Default attributes** (always included):
|
|
312
|
+
- `className` (React, JSX)
|
|
313
|
+
- `class` (HTML, Web Components)
|
|
314
|
+
- `classList` (Solid.js)
|
|
315
|
+
|
|
316
|
+
**Example - Add React Native style attributes**:
|
|
317
|
+
```json
|
|
318
|
+
{
|
|
319
|
+
"classAttributes": {
|
|
320
|
+
"attributes": ["colorStyles", "textStyles", "containerStyles"]
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Usage example**:
|
|
326
|
+
```tsx
|
|
327
|
+
// ✅ All of these will be validated:
|
|
328
|
+
<div className="flex items-center">...</div>
|
|
329
|
+
<div class="flex items-center">...</div>
|
|
330
|
+
<div classList={{ flex: true }}>...</div>
|
|
331
|
+
<View colorStyles="bg-blue-500 text-white">...</View>
|
|
332
|
+
<Text textStyles="font-bold text-lg">...</Text>
|
|
333
|
+
```
|
|
141
334
|
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
// Simple calls (validated by default):
|
|
145
|
-
className={clsx('flex', 'items-center')}
|
|
146
|
-
className={cn('flex', 'items-center')}
|
|
335
|
+
---
|
|
147
336
|
|
|
148
|
-
|
|
149
|
-
className={utils.cn('flex', 'items-center')}
|
|
150
|
-
className={lib.clsx('flex', 'items-center')}
|
|
337
|
+
### Backwards Compatibility
|
|
151
338
|
|
|
152
|
-
|
|
153
|
-
className={myCustomFn('flex', 'items-center')}
|
|
154
|
-
className={buildClasses('flex', 'items-center')}
|
|
339
|
+
The following deprecated options are still supported but will be removed in a future version:
|
|
155
340
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
341
|
+
| Deprecated | New Location |
|
|
342
|
+
|------------|--------------|
|
|
343
|
+
| `utilityFunctions` | `libraries.utilities` |
|
|
344
|
+
| `variants` | `libraries.variants` |
|
|
345
|
+
| `allowedClasses` | `validation.allowedClasses` |
|
|
346
|
+
| `enableLogging` | Removed (no replacement) |
|
|
159
347
|
|
|
160
348
|
### 2. Ensure your CSS file imports Tailwind
|
|
161
349
|
|
|
@@ -188,6 +376,49 @@ You may need to restart the TypeScript server:
|
|
|
188
376
|
- Open command palette
|
|
189
377
|
- Type "TypeScript: Restart TS Server"
|
|
190
378
|
|
|
379
|
+
#### Vue Projects
|
|
380
|
+
|
|
381
|
+
For Vue Single File Components (`.vue` files), you need to configure `@vue/typescript-plugin` alongside this plugin:
|
|
382
|
+
|
|
383
|
+
1. Install the Vue TypeScript plugin:
|
|
384
|
+
```bash
|
|
385
|
+
npm install -D @vue/typescript-plugin
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
2. Configure both plugins in your `tsconfig.json`:
|
|
389
|
+
```json
|
|
390
|
+
{
|
|
391
|
+
"compilerOptions": {
|
|
392
|
+
"plugins": [
|
|
393
|
+
{
|
|
394
|
+
"name": "@vue/typescript-plugin",
|
|
395
|
+
"languages": ["vue"]
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
"name": "tailwind-typescript-plugin",
|
|
399
|
+
"globalCss": "./src/global.css"
|
|
400
|
+
}
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
3. Ensure you have the [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) extension installed in VS Code.
|
|
407
|
+
|
|
408
|
+
The plugin will then validate classes in your Vue templates:
|
|
409
|
+
```vue
|
|
410
|
+
<template>
|
|
411
|
+
<!-- ✅ Valid classes -->
|
|
412
|
+
<div class="flex items-center">Valid</div>
|
|
413
|
+
|
|
414
|
+
<!-- ❌ Invalid class detected -->
|
|
415
|
+
<div class="invalidclass">Error shown</div>
|
|
416
|
+
|
|
417
|
+
<!-- ✅ Dynamic class bindings -->
|
|
418
|
+
<div :class="{ 'bg-red-500': isActive }">Dynamic</div>
|
|
419
|
+
</template>
|
|
420
|
+
```
|
|
421
|
+
|
|
191
422
|
#### Other Editors
|
|
192
423
|
|
|
193
424
|
Most editors that support TypeScript Language Service plugins should work automatically. Refer to your editor's documentation for TypeScript plugin configuration.
|
|
@@ -245,6 +476,18 @@ const validClass = 'flex items-center';
|
|
|
245
476
|
const baseClass = 'flex';
|
|
246
477
|
<div className={[baseClass, 'items-center']}>Array with variable</div>
|
|
247
478
|
|
|
479
|
+
// ✅ Spread operator in arrays
|
|
480
|
+
const baseClasses = ['flex', 'items-center'];
|
|
481
|
+
<div className={[...baseClasses, 'p-4']}>Spread in array</div>
|
|
482
|
+
|
|
483
|
+
// ✅ Spread operator in function calls
|
|
484
|
+
<div className={cn(...baseClasses, 'p-4')}>Spread in function</div>
|
|
485
|
+
|
|
486
|
+
// ✅ Multiple spreads
|
|
487
|
+
const layoutClasses = ['flex', 'items-center'];
|
|
488
|
+
const spacingClasses = ['p-4', 'm-2'];
|
|
489
|
+
<div className={cn(...layoutClasses, ...spacingClasses)}>Multiple spreads</div>
|
|
490
|
+
|
|
248
491
|
// ✅ Variables in tv() and cva()
|
|
249
492
|
const buttonBase = 'font-semibold text-white';
|
|
250
493
|
const button = tv({ base: buttonBase });
|
|
@@ -275,6 +518,14 @@ const badClass = 'invalid-array-class';
|
|
|
275
518
|
// Error: The class "invalid-array-class" is not a valid Tailwind class.
|
|
276
519
|
// This value is used as className via variable "badClass" on line 8
|
|
277
520
|
<div className={['flex', badClass]}>Array with invalid variable</div>
|
|
521
|
+
|
|
522
|
+
// ❌ Invalid class in spread operator
|
|
523
|
+
const invalidClasses = ['flex', 'invalid-spread-class'];
|
|
524
|
+
// Error: The class "invalid-spread-class" is not a valid Tailwind class.
|
|
525
|
+
<div className={[...invalidClasses, 'items-center']}>Spread with invalid</div>
|
|
526
|
+
|
|
527
|
+
// ❌ Invalid class in spread within function call
|
|
528
|
+
<div className={cn(...invalidClasses, 'p-4')}>Function spread with invalid</div>
|
|
278
529
|
```
|
|
279
530
|
|
|
280
531
|
**Custom allowed classes**:
|
|
@@ -326,6 +577,18 @@ The plugin detects duplicate classes within the same `className` attribute and s
|
|
|
326
577
|
<div className={cn(['flex', 'flex', 'items-center'])}>In array</div>
|
|
327
578
|
// Warning: Duplicate class "flex"
|
|
328
579
|
|
|
580
|
+
// ⚠️ Warning: Duplicates with spread operators
|
|
581
|
+
const baseClasses = ['flex', 'items-center'];
|
|
582
|
+
<div className={cn(...baseClasses, 'flex', 'items-center', 'p-4')}>Spread duplicates</div>
|
|
583
|
+
// Warning: Duplicate class "flex"
|
|
584
|
+
// Warning: Duplicate class "items-center"
|
|
585
|
+
|
|
586
|
+
// ⚠️ Warning: Duplicates between multiple spreads
|
|
587
|
+
const layoutClasses = ['flex', 'items-center'];
|
|
588
|
+
const containerClasses = ['flex', 'justify-center'];
|
|
589
|
+
<div className={cn(...layoutClasses, ...containerClasses)}>Multiple spread duplicates</div>
|
|
590
|
+
// Warning: Duplicate class "flex"
|
|
591
|
+
|
|
329
592
|
// ✅ Valid: Same class in DIFFERENT elements (not duplicates)
|
|
330
593
|
<div className="flex items-center">
|
|
331
594
|
<span className="flex justify-center">Different elements</span>
|
|
@@ -479,6 +742,22 @@ The plugin intelligently handles ternary expressions - conflicts are NOT flagged
|
|
|
479
742
|
// Warning: Both classes affect text-align
|
|
480
743
|
```
|
|
481
744
|
|
|
745
|
+
**Conflicts with spread operators**:
|
|
746
|
+
|
|
747
|
+
```tsx
|
|
748
|
+
// ⚠️ Warning: Conflicts with spread operator
|
|
749
|
+
const baseClasses = ['flex', 'p-4'];
|
|
750
|
+
<div className={cn(...baseClasses, 'p-2', 'items-center')}>Spread conflict</div>
|
|
751
|
+
// Warning: Class "p-4" conflicts with "p-2". Both affect the padding property.
|
|
752
|
+
|
|
753
|
+
// ⚠️ Warning: Conflicts between multiple spreads
|
|
754
|
+
const smallText = ['text-sm', 'font-medium'];
|
|
755
|
+
const largeText = ['text-lg', 'font-bold'];
|
|
756
|
+
<div className={cn(...smallText, ...largeText)}>Multiple spread conflicts</div>
|
|
757
|
+
// Warning: Class "text-sm" conflicts with "text-lg". Both affect the font-size property.
|
|
758
|
+
// Warning: Class "font-medium" conflicts with "font-bold". Both affect the font-weight property.
|
|
759
|
+
```
|
|
760
|
+
|
|
482
761
|
**Conflicts in tv() and cva()**:
|
|
483
762
|
|
|
484
763
|
The plugin detects conflicts within `tv()` and `cva()` base properties, but intelligently skips conflicts between base and variants since variants are designed to override base styles:
|
|
@@ -834,6 +1113,15 @@ const card2 = tv({ base: 'flex justify-center' });
|
|
|
834
1113
|
Validates variables used in computed object property keys
|
|
835
1114
|
Example: `const myClass = 'invalid-class'; <div className={{ [myClass]: true }}>Object</div>`
|
|
836
1115
|
|
|
1116
|
+
- [X] **Spread Operator** → [`test-spread-operator/`](./example/src/test-spread-operator/)
|
|
1117
|
+
Validates spread operators in arrays and function calls
|
|
1118
|
+
Example: `const base = ['flex', 'invalid']; <div className={[...base, 'p-4']}>Spread</div>`
|
|
1119
|
+
- Spread in arrays: `className={[...baseClasses, 'p-4']}`
|
|
1120
|
+
- Spread in function calls: `className={cn(...baseClasses, 'p-4')}`
|
|
1121
|
+
- Multiple spreads: `className={cn(...layoutClasses, ...spacingClasses)}`
|
|
1122
|
+
- Nested spreads: Variables containing arrays with spreads are resolved
|
|
1123
|
+
- Detects invalid classes, duplicates, and conflicts within spread expressions
|
|
1124
|
+
|
|
837
1125
|
- [X] **TV Variable** → [`tv-variable.tsx`](./example/src/tv-variable.tsx)
|
|
838
1126
|
Validates variables in tailwind-variants tv() definitions
|
|
839
1127
|
Example: `const baseClasses = 'invalid-class'; const button = tv({ base: baseClasses })`
|
|
@@ -842,19 +1130,19 @@ const card2 = tv({ base: 'flex justify-center' });
|
|
|
842
1130
|
Validates variables in class-variance-authority cva() definitions
|
|
843
1131
|
Example: `const baseClasses = 'invalid-class'; const button = cva(baseClasses)`
|
|
844
1132
|
|
|
845
|
-
- [X] **Duplicate Classes** → [`duplicate-classes
|
|
1133
|
+
- [X] **Duplicate Classes** → [`duplicate-classes/`](./example/src/duplicate-classes/)
|
|
846
1134
|
Detects duplicate classes within the same className attribute
|
|
847
|
-
Example: `className="flex flex items-center"` shows warning on
|
|
1135
|
+
Example: `className="flex flex items-center"` shows warning on both `flex` occurrences
|
|
848
1136
|
|
|
849
|
-
- [X] **Duplicate Classes in Ternary** → [`duplicate-classes
|
|
1137
|
+
- [X] **Duplicate Classes in Ternary** → [`duplicate-classes/`](./example/src/duplicate-classes/)
|
|
850
1138
|
Smart detection of duplicates in ternary expressions:
|
|
851
|
-
- Root + branch duplicate: `clsx('flex', isActive ? 'flex' : 'flex')` → Warning
|
|
1139
|
+
- Root + branch duplicate: `clsx('flex', isActive ? 'flex' : 'flex')` → Warning on all occurrences
|
|
852
1140
|
- Both branches same class: `clsx('mt-4', isActive ? 'flex' : 'flex')` → Warning (consider extracting)
|
|
853
1141
|
- Single branch only: `clsx('mt-4', isActive ? 'flex' : '')` → No warning (valid pattern)
|
|
854
1142
|
|
|
855
|
-
- [X] **Duplicate Classes in Variables with Conditionals** → [`duplicate-classes
|
|
1143
|
+
- [X] **Duplicate Classes in Variables with Conditionals** → [`duplicate-classes/`](./example/src/duplicate-classes/)
|
|
856
1144
|
Resolves variables containing ternary expressions and detects duplicates:
|
|
857
|
-
- `const x = isActive ? 'flex' : 'flex'; clsx('flex', x)` → Warning
|
|
1145
|
+
- `const x = isActive ? 'flex' : 'flex'; clsx('flex', x)` → Warning on all occurrences
|
|
858
1146
|
- `const x = isActive ? 'flex' : 'flex'; clsx('mt-4', x)` → Warning (consider extracting from variable)
|
|
859
1147
|
- `const x = isActive ? 'flex' : ''; clsx('mt-4', x)` → No warning (single branch)
|
|
860
1148
|
|
|
@@ -879,6 +1167,16 @@ const card2 = tv({ base: 'flex justify-center' });
|
|
|
879
1167
|
- Works with clsx, cn, tv(), cva() and other utility functions
|
|
880
1168
|
- tv()/cva() base vs variant: NO conflict (variants are designed to override base)
|
|
881
1169
|
|
|
1170
|
+
- [X] **Utility Function Import Verification** → [`utility-function-imports/`](./example/src/utility-function-imports/)
|
|
1171
|
+
Validates custom utility functions with optional import source verification
|
|
1172
|
+
Example: `{ "name": "merge", "from": "@/lib/utils" }` only validates `merge()` if imported from `@/lib/utils`
|
|
1173
|
+
- Supports simple string format (name-only matching) and object format (with import verification)
|
|
1174
|
+
- Named imports: `import { merge } from '@/lib/utils'`
|
|
1175
|
+
- Default imports: `import merge from '@/lib/utils'`
|
|
1176
|
+
- Aliased imports: `import { something as merge } from '@/lib/utils'`
|
|
1177
|
+
- Subpath matching: `{ "from": "my-pkg" }` matches `import from 'my-pkg/utils'`
|
|
1178
|
+
- Functions from wrong import sources are skipped (prevents false positives)
|
|
1179
|
+
|
|
882
1180
|
## How It Works
|
|
883
1181
|
|
|
884
1182
|
The plugin hooks into the TypeScript Language Service and:
|
|
@@ -902,6 +1200,34 @@ The plugin is designed for minimal performance impact:
|
|
|
902
1200
|
|
|
903
1201
|
**Typical overhead**: <1ms per file for most files, ~2-3ms for files with many tv()/cva() calls
|
|
904
1202
|
|
|
1203
|
+
## Hot Reloading
|
|
1204
|
+
|
|
1205
|
+
The plugin automatically watches your global CSS file for changes. When you modify your Tailwind configuration or add custom classes in your CSS file, the plugin will:
|
|
1206
|
+
|
|
1207
|
+
1. **Detect changes** - Watches the `globalCss` file specified in your `tsconfig.json`
|
|
1208
|
+
2. **Reload the design system** - Automatically re-parses your Tailwind configuration
|
|
1209
|
+
3. **Clear caches** - Invalidates all diagnostic caches
|
|
1210
|
+
4. **Refresh diagnostics** - Requests TypeScript to revalidate all open files
|
|
1211
|
+
|
|
1212
|
+
This means you can add new custom classes or modify your Tailwind theme, and the plugin will immediately recognize them without restarting your editor or TypeScript server.
|
|
1213
|
+
|
|
1214
|
+
```css
|
|
1215
|
+
/* global.css */
|
|
1216
|
+
@import "tailwindcss";
|
|
1217
|
+
|
|
1218
|
+
/* Add a custom utility - plugin will recognize it after save */
|
|
1219
|
+
@utility custom-gradient {
|
|
1220
|
+
background: linear-gradient(to right, #ff7e5f, #feb47b);
|
|
1221
|
+
}
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
```tsx
|
|
1225
|
+
// This will be valid immediately after saving global.css
|
|
1226
|
+
<div className="custom-gradient">Hot reloaded!</div>
|
|
1227
|
+
```
|
|
1228
|
+
|
|
1229
|
+
**Note**: The file watcher uses debouncing (300ms) to avoid excessive reloads when making rapid changes.
|
|
1230
|
+
|
|
905
1231
|
## Development
|
|
906
1232
|
|
|
907
1233
|
This is a monorepo using Yarn workspaces:
|
|
@@ -944,7 +1270,7 @@ Commit to main → Auto-publishes 1.0.33-beta.3
|
|
|
944
1270
|
|
|
945
1271
|
Users can install beta versions:
|
|
946
1272
|
```bash
|
|
947
|
-
npm install typescript-
|
|
1273
|
+
npm install tailwind-typescript-plugin@beta
|
|
948
1274
|
```
|
|
949
1275
|
|
|
950
1276
|
### Stable Releases (Manual)
|
package/lib/core/interfaces.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript/lib/tsserverlibrary';
|
|
2
|
-
import { ClassNameInfo, ExtractionContext } from './types';
|
|
2
|
+
import { ClassAttributesConfig, ClassNameInfo, EditorConfig, ExtractionContext, LibrariesConfig, LintConfig, ValidationConfig } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Base interface for class name extractors
|
|
5
5
|
* Follows the Strategy pattern for extensibility
|
|
@@ -22,22 +22,33 @@ export interface IClassNameValidator {
|
|
|
22
22
|
isInitialized(): boolean;
|
|
23
23
|
setAllowedClasses(allowedClasses: string[]): void;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Interface for variant library configuration
|
|
27
|
-
*/
|
|
28
|
-
export interface IVariantsConfig {
|
|
29
|
-
tailwindVariants?: boolean;
|
|
30
|
-
classVarianceAuthority?: boolean;
|
|
31
|
-
}
|
|
32
25
|
/**
|
|
33
26
|
* Interface for configuration management
|
|
34
27
|
*/
|
|
35
28
|
export interface IPluginConfig {
|
|
36
29
|
globalCss?: string;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Library configurations (utilities and variants)
|
|
32
|
+
*/
|
|
33
|
+
libraries?: LibrariesConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Validation configuration (invalid class detection)
|
|
36
|
+
*/
|
|
37
|
+
validation?: ValidationConfig;
|
|
38
|
+
/**
|
|
39
|
+
* Lint configuration (conflicting and repeated classes)
|
|
40
|
+
*/
|
|
41
|
+
lint?: LintConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Editor features configuration (autocomplete and hover)
|
|
44
|
+
*/
|
|
45
|
+
editor?: EditorConfig;
|
|
46
|
+
/**
|
|
47
|
+
* Additional attribute names to treat as class attributes
|
|
48
|
+
* These are merged with defaults (className, class, classList)
|
|
49
|
+
* Example: ["containerStyles", "textStyles"]
|
|
50
|
+
*/
|
|
51
|
+
classAttributes?: ClassAttributesConfig;
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
43
54
|
* Interface for diagnostic creation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/core/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAErD,OAAO,
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/core/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAErD,OAAO,EACN,qBAAqB,EACrB,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IACnC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAE9D;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,aAAa,EAAE,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,aAAa,IAAI,OAAO,CAAC;IACzB,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAClD;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;OAEG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAE9B;;OAEG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;;;OAIG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;CACrF"}
|