tailwind-typescript-plugin 1.4.1-beta.2 → 1.4.1-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.
Files changed (108) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/README.md +375 -77
  3. package/lib/core/interfaces.d.ts +23 -12
  4. package/lib/core/interfaces.d.ts.map +1 -1
  5. package/lib/core/types.d.ts +112 -1
  6. package/lib/core/types.d.ts.map +1 -1
  7. package/lib/extractors/BaseExtractor.d.ts +49 -2
  8. package/lib/extractors/BaseExtractor.d.ts.map +1 -1
  9. package/lib/extractors/BaseExtractor.js +184 -5
  10. package/lib/extractors/BaseExtractor.js.map +1 -1
  11. package/lib/extractors/CvaExtractor.js +1 -1
  12. package/lib/extractors/CvaExtractor.js.map +1 -1
  13. package/lib/extractors/ExpressionExtractor.d.ts.map +1 -1
  14. package/lib/extractors/ExpressionExtractor.js +35 -5
  15. package/lib/extractors/ExpressionExtractor.js.map +1 -1
  16. package/lib/extractors/JsxAttributeExtractor.d.ts +2 -1
  17. package/lib/extractors/JsxAttributeExtractor.d.ts.map +1 -1
  18. package/lib/extractors/JsxAttributeExtractor.js +15 -8
  19. package/lib/extractors/JsxAttributeExtractor.js.map +1 -1
  20. package/lib/extractors/SvelteAttributeExtractor.d.ts +17 -0
  21. package/lib/extractors/SvelteAttributeExtractor.d.ts.map +1 -0
  22. package/lib/extractors/SvelteAttributeExtractor.js +27 -0
  23. package/lib/extractors/SvelteAttributeExtractor.js.map +1 -0
  24. package/lib/extractors/TailwindVariantsExtractor.d.ts.map +1 -1
  25. package/lib/extractors/TailwindVariantsExtractor.js +1 -5
  26. package/lib/extractors/TailwindVariantsExtractor.js.map +1 -1
  27. package/lib/extractors/VariableReferenceExtractor.d.ts.map +1 -1
  28. package/lib/extractors/VariableReferenceExtractor.js +21 -0
  29. package/lib/extractors/VariableReferenceExtractor.js.map +1 -1
  30. package/lib/extractors/VueAttributeExtractor.d.ts +119 -0
  31. package/lib/extractors/VueAttributeExtractor.d.ts.map +1 -0
  32. package/lib/extractors/VueAttributeExtractor.js +911 -0
  33. package/lib/extractors/VueAttributeExtractor.js.map +1 -0
  34. package/lib/extractors/VueExpressionExtractor.d.ts +21 -0
  35. package/lib/extractors/VueExpressionExtractor.d.ts.map +1 -0
  36. package/lib/extractors/VueExpressionExtractor.js +51 -0
  37. package/lib/extractors/VueExpressionExtractor.js.map +1 -0
  38. package/lib/infrastructure/TailwindConflictDetector.d.ts +28 -18
  39. package/lib/infrastructure/TailwindConflictDetector.d.ts.map +1 -1
  40. package/lib/infrastructure/TailwindConflictDetector.js +216 -486
  41. package/lib/infrastructure/TailwindConflictDetector.js.map +1 -1
  42. package/lib/infrastructure/TailwindValidator.css-vars.spec.js +1 -11
  43. package/lib/infrastructure/TailwindValidator.css-vars.spec.js.map +1 -1
  44. package/lib/infrastructure/TailwindValidator.d.ts +16 -3
  45. package/lib/infrastructure/TailwindValidator.d.ts.map +1 -1
  46. package/lib/infrastructure/TailwindValidator.js +83 -24
  47. package/lib/infrastructure/TailwindValidator.js.map +1 -1
  48. package/lib/infrastructure/TailwindValidator.spec.js +50 -17
  49. package/lib/infrastructure/TailwindValidator.spec.js.map +1 -1
  50. package/lib/plugin/TailwindTypescriptPlugin.d.ts +18 -1
  51. package/lib/plugin/TailwindTypescriptPlugin.d.ts.map +1 -1
  52. package/lib/plugin/TailwindTypescriptPlugin.js +124 -55
  53. package/lib/plugin/TailwindTypescriptPlugin.js.map +1 -1
  54. package/lib/services/ClassNameExtractionService.d.ts +21 -5
  55. package/lib/services/ClassNameExtractionService.d.ts.map +1 -1
  56. package/lib/services/ClassNameExtractionService.js +76 -14
  57. package/lib/services/ClassNameExtractionService.js.map +1 -1
  58. package/lib/services/ClassNameExtractionService.spec.d.ts +2 -0
  59. package/lib/services/ClassNameExtractionService.spec.d.ts.map +1 -0
  60. package/lib/services/ClassNameExtractionService.spec.js +222 -0
  61. package/lib/services/ClassNameExtractionService.spec.js.map +1 -0
  62. package/lib/services/CodeActionService.spec.js +1 -2
  63. package/lib/services/CodeActionService.spec.js.map +1 -1
  64. package/lib/services/CompletionService.d.ts +121 -0
  65. package/lib/services/CompletionService.d.ts.map +1 -0
  66. package/lib/services/CompletionService.js +573 -0
  67. package/lib/services/CompletionService.js.map +1 -0
  68. package/lib/services/CompletionService.spec.d.ts +2 -0
  69. package/lib/services/CompletionService.spec.d.ts.map +1 -0
  70. package/lib/services/CompletionService.spec.js +1182 -0
  71. package/lib/services/CompletionService.spec.js.map +1 -0
  72. package/lib/services/ConflictClassDetection.spec.js +26 -21
  73. package/lib/services/ConflictClassDetection.spec.js.map +1 -1
  74. package/lib/services/DiagnosticService.d.ts +8 -8
  75. package/lib/services/DiagnosticService.d.ts.map +1 -1
  76. package/lib/services/DiagnosticService.js +29 -13
  77. package/lib/services/DiagnosticService.js.map +1 -1
  78. package/lib/services/DuplicateClassDetection.spec.js +61 -81
  79. package/lib/services/DuplicateClassDetection.spec.js.map +1 -1
  80. package/lib/services/PluginConfigService.d.ts +47 -15
  81. package/lib/services/PluginConfigService.d.ts.map +1 -1
  82. package/lib/services/PluginConfigService.js +203 -75
  83. package/lib/services/PluginConfigService.js.map +1 -1
  84. package/lib/services/PluginConfigService.spec.d.ts +2 -0
  85. package/lib/services/PluginConfigService.spec.d.ts.map +1 -0
  86. package/lib/services/PluginConfigService.spec.js +93 -0
  87. package/lib/services/PluginConfigService.spec.js.map +1 -0
  88. package/lib/services/ValidationService.d.ts +8 -5
  89. package/lib/services/ValidationService.d.ts.map +1 -1
  90. package/lib/services/ValidationService.js +49 -49
  91. package/lib/services/ValidationService.js.map +1 -1
  92. package/lib/utils/FrameworkDetector.d.ts +24 -0
  93. package/lib/utils/FrameworkDetector.d.ts.map +1 -0
  94. package/lib/utils/FrameworkDetector.js +52 -0
  95. package/lib/utils/FrameworkDetector.js.map +1 -0
  96. package/lib/utils/FrameworkDetector.spec.d.ts +2 -0
  97. package/lib/utils/FrameworkDetector.spec.d.ts.map +1 -0
  98. package/lib/utils/FrameworkDetector.spec.js +75 -0
  99. package/lib/utils/FrameworkDetector.spec.js.map +1 -0
  100. package/package.json +4 -2
  101. package/lib/extractors/StringLiteralExtractor.d.ts +0 -12
  102. package/lib/extractors/StringLiteralExtractor.d.ts.map +0 -1
  103. package/lib/extractors/StringLiteralExtractor.js +0 -21
  104. package/lib/extractors/StringLiteralExtractor.js.map +0 -1
  105. package/lib/services/ClassNameExtractionService.original.d.ts +0 -20
  106. package/lib/services/ClassNameExtractionService.original.d.ts.map +0 -1
  107. package/lib/services/ClassNameExtractionService.original.js +0 -48
  108. package/lib/services/ClassNameExtractionService.original.js.map +0 -1
package/README.md CHANGED
@@ -6,11 +6,11 @@
6
6
  [![TypeScript](https://img.shields.io/github/package-json/dependency-version/IvanRodriCalleja/tailwind-typescript-plugin/dev/typescript?label=TypeScript)](https://www.typescriptlang.org/)
7
7
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](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 and validating that all Tailwind classes used in `className` 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.
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
- "utilityFunctions": ["clsx", "cn", "classnames"],
70
- "allowedClasses": ["custom-button", "app-header", "project-card"],
71
- "variants": {
72
- "tailwindVariants": true,
73
- "classVarianceAuthority": true
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
- - `globalCss` (required): Path to your global CSS file that imports Tailwind CSS. This can be relative to your project root.
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
- - `allowedClasses` (optional): Array of custom class names that should be treated as valid alongside Tailwind classes. Useful for project-specific or third-party utility classes that aren't part of Tailwind.
86
- - **Default**: `[]` (no custom classes allowed)
87
- - **Example**:
88
- ```json
89
- {
90
- "allowedClasses": ["custom-button", "app-header", "project-card"]
91
- }
92
- ```
93
- - Classes in this list will be considered valid and won't trigger validation errors
94
- - Works with all extraction patterns (literals, expressions, functions, arrays, etc.)
95
- - Combines with Tailwind classes - both are validated independently
96
-
97
- - `variants` (optional): Configure which variant library extractors to enable. This is useful for performance optimization when you only use one library.
98
- - **Default behavior (no config)**: Both `tailwind-variants` and `class-variance-authority` are enabled
99
- - **Selective enabling**: If you specify ANY variant config, only those explicitly set to `true` are enabled
100
- - **Example configurations**:
101
- ```json
102
- // Enable only tailwind-variants
103
- {
104
- "variants": {
105
- "tailwindVariants": true
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
- // Enable only class-variance-authority
110
- {
111
- "variants": {
112
- "classVarianceAuthority": true
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
- // Enable both explicitly
117
- {
118
- "variants": {
119
- "tailwindVariants": true,
120
- "classVarianceAuthority": true
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
- // No config = both enabled by default
125
- {
126
- // variants not specified - both libraries validated
267
+ **Example - Make duplicate classes an error**:
268
+ ```json
269
+ {
270
+ "lint": {
271
+ "repeatedClasses": {
272
+ "severity": "error"
127
273
  }
128
- ```
129
- - **Performance impact**: Disabling unused extractors skips TypeChecker operations and symbol resolution for that library, providing faster validation
130
-
131
- - `utilityFunctions` (optional): Array of additional function names to validate. These will be **merged with the defaults**, so you don't lose the common ones.
132
- - **Defaults (always included)**: `['clsx', 'cn', 'classnames', 'classNames', 'cx', 'cva', 'twMerge', 'tv']`
133
- - **Add your own**: Provide custom function names that will be added to the defaults
134
- - **Example config**:
135
- ```json
136
- {
137
- "utilityFunctions": ["myCustomFn", "buildClasses"]
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
- This will validate: `clsx`, `cn`, `classnames`, `classNames`, `cx`, `cva`, `twMerge`, `tv`, **`myCustomFn`**, **`buildClasses`**
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)
141
315
 
142
- - **Supported patterns**:
143
- ```typescript
144
- // Simple calls (validated by default):
145
- className={clsx('flex', 'items-center')}
146
- className={cn('flex', 'items-center')}
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
+ ```
147
334
 
148
- // Member expressions (nested property access):
149
- className={utils.cn('flex', 'items-center')}
150
- className={lib.clsx('flex', 'items-center')}
335
+ ---
151
336
 
152
- // Custom functions (add via config):
153
- className={myCustomFn('flex', 'items-center')}
154
- className={buildClasses('flex', 'items-center')}
337
+ ### Backwards Compatibility
155
338
 
156
- // Dynamic calls (ignored, won't throw errors):
157
- className={functions['cn']('flex', 'items-center')}
158
- ```
339
+ The following deprecated options are still supported but will be removed in a future version:
340
+
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.tsx`](./example/src/duplicate-classes.tsx)
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 second `flex`
1135
+ Example: `className="flex flex items-center"` shows warning on both `flex` occurrences
848
1136
 
849
- - [X] **Duplicate Classes in Ternary** → [`duplicate-classes.tsx`](./example/src/duplicate-classes.tsx)
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 (true duplicate)
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.tsx`](./example/src/duplicate-classes.tsx)
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 (root + variable duplicate)
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:
@@ -972,7 +1270,7 @@ Commit to main → Auto-publishes 1.0.33-beta.3
972
1270
 
973
1271
  Users can install beta versions:
974
1272
  ```bash
975
- npm install typescript-custom-plugin@beta
1273
+ npm install tailwind-typescript-plugin@beta
976
1274
  ```
977
1275
 
978
1276
  ### Stable Releases (Manual)
@@ -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
- utilityFunctions?: string[];
38
- variants?: IVariantsConfig;
39
- allowedClasses?: string[];
40
- enableLogging?: boolean;
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,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE3D;;;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,eAAe;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,gBAAgB,CAAC,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;CACrF"}
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"}