Skip to content

Commit a5b9836

Browse files
committed
feat: add support for invalid class diagnostics and update settings
1 parent 072a6c9 commit a5b9836

File tree

5 files changed

+94
-1
lines changed

5 files changed

+94
-1
lines changed

packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOr
1111
import { getInvalidSourceDiagnostics } from './getInvalidSourceDiagnostics'
1212
import { getUsedBlocklistedClassDiagnostics } from './getUsedBlocklistedClassDiagnostics'
1313
import { getSuggestCanonicalClassesDiagnostics } from './canonical-classes'
14+
import { getInvalidClassDiagnostics } from './getInvalidClassDiagnostics'
1415

1516
export async function doValidate(
1617
state: State,
@@ -26,6 +27,7 @@ export async function doValidate(
2627
DiagnosticKind.RecommendedVariantOrder,
2728
DiagnosticKind.UsedBlocklistedClass,
2829
DiagnosticKind.SuggestCanonicalClasses,
30+
DiagnosticKind.InvalidClass,
2931
],
3032
): Promise<AugmentedDiagnostic[]> {
3133
const settings = await state.editor.getConfiguration(document.uri)
@@ -62,6 +64,9 @@ export async function doValidate(
6264
...(only.includes(DiagnosticKind.SuggestCanonicalClasses)
6365
? await getSuggestCanonicalClassesDiagnostics(state, document, settings)
6466
: []),
67+
...(only.includes(DiagnosticKind.InvalidClass)
68+
? await getInvalidClassDiagnostics(state, document, settings)
69+
: []),
6570
]
6671
: []
6772
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import type { State, Settings, DocumentClassName } from '../util/state'
2+
import { type InvalidClassDiagnostic, DiagnosticKind } from './types'
3+
import { findClassListsInDocument, getClassNamesInClassList } from '../util/find'
4+
import { visit } from './getCssConflictDiagnostics'
5+
import type { TextDocument } from 'vscode-languageserver-textdocument'
6+
7+
function isClassValid(state: State, className: string): boolean {
8+
if (!state.v4) return true // Only check for v4
9+
10+
let roots = state.designSystem.compile([className])
11+
let hasDeclarations = false
12+
13+
visit([roots[0]], (node) => {
14+
if ((node.type === 'rule' || node.type === 'atrule') && node.nodes) {
15+
for (let child of node.nodes) {
16+
if (child.type === 'decl') {
17+
hasDeclarations = true
18+
break
19+
}
20+
}
21+
}
22+
})
23+
24+
return hasDeclarations
25+
}
26+
27+
export async function getInvalidClassDiagnostics(
28+
state: State,
29+
document: TextDocument,
30+
settings: Settings,
31+
): Promise<InvalidClassDiagnostic[]> {
32+
let severity = settings.tailwindCSS.lint.invalidClass || 'warning' // Assuming a new setting, default to warning
33+
if (severity === 'ignore') return []
34+
35+
let diagnostics: InvalidClassDiagnostic[] = []
36+
const classLists = await findClassListsInDocument(state, document)
37+
38+
classLists.forEach((classList) => {
39+
const classNames = getClassNamesInClassList(classList, state.blocklist)
40+
41+
classNames.forEach((className) => {
42+
if (!isClassValid(state, className.className)) {
43+
diagnostics.push({
44+
code: DiagnosticKind.InvalidClass,
45+
source: 'tailwindcss',
46+
className,
47+
range: className.range,
48+
severity:
49+
severity === 'error'
50+
? 1 /* DiagnosticSeverity.Error */
51+
: severity === 'warning'
52+
? 2 /* DiagnosticSeverity.Warning */
53+
: 3 /* DiagnosticSeverity.Information */,
54+
message: `'${className.className}' is not a recognized Tailwind CSS class.`,
55+
})
56+
}
57+
})
58+
})
59+
60+
return diagnostics
61+
}

packages/tailwindcss-language-service/src/diagnostics/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export enum DiagnosticKind {
1212
RecommendedVariantOrder = 'recommendedVariantOrder',
1313
UsedBlocklistedClass = 'usedBlocklistedClass',
1414
SuggestCanonicalClasses = 'suggestCanonicalClasses',
15+
InvalidClass = 'invalidClass',
1516
}
1617

1718
export type CssConflictDiagnostic = Diagnostic & {
@@ -123,6 +124,17 @@ export function isSuggestCanonicalClasses(
123124
return diagnostic.code === DiagnosticKind.SuggestCanonicalClasses
124125
}
125126

127+
export type InvalidClassDiagnostic = Diagnostic & {
128+
code: DiagnosticKind.InvalidClass
129+
className: DocumentClassName
130+
}
131+
132+
export function isInvalidClassDiagnostic(
133+
diagnostic: AugmentedDiagnostic,
134+
): diagnostic is InvalidClassDiagnostic {
135+
return diagnostic.code === DiagnosticKind.InvalidClass
136+
}
137+
126138
export type AugmentedDiagnostic =
127139
| CssConflictDiagnostic
128140
| InvalidApplyDiagnostic
@@ -134,3 +146,4 @@ export type AugmentedDiagnostic =
134146
| RecommendedVariantOrderDiagnostic
135147
| UsedBlocklistedClassDiagnostic
136148
| SuggestCanonicalClassesDiagnostic
149+
| InvalidClassDiagnostic

packages/tailwindcss-language-service/src/util/state.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export type EditorState = {
3636
) => Promise<Array<[string, { isDirectory: boolean }]>>
3737
}
3838

39-
type DiagnosticSeveritySetting = 'ignore' | 'warning' | 'error'
39+
type DiagnosticSeveritySetting = 'ignore' | 'info' | 'warning' | 'error'
4040

4141
export type EditorSettings = {
4242
tabSize: number
@@ -67,6 +67,7 @@ export type TailwindCssSettings = {
6767
recommendedVariantOrder: DiagnosticSeveritySetting
6868
usedBlocklistedClass: DiagnosticSeveritySetting
6969
suggestCanonicalClasses: DiagnosticSeveritySetting
70+
invalidClass: DiagnosticSeveritySetting
7071
}
7172
experimental: {
7273
classRegex: string[] | [string, string][]
@@ -207,6 +208,7 @@ export function getDefaultTailwindSettings(): Settings {
207208
recommendedVariantOrder: 'warning',
208209
usedBlocklistedClass: 'warning',
209210
suggestCanonicalClasses: 'warning',
211+
invalidClass: 'warning',
210212
},
211213
showPixelEquivalents: true,
212214
includeLanguages: {},

packages/vscode-tailwindcss/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,18 @@
327327
"markdownDescription": "Indicate when utilities may be written in a more optimal form",
328328
"scope": "language-overridable"
329329
},
330+
"tailwindCSS.lint.invalidClass": {
331+
"type": "string",
332+
"enum": [
333+
"ignore",
334+
"info",
335+
"warning",
336+
"error"
337+
],
338+
"default": "info",
339+
"markdownDescription": "Classes that are not recognized as valid Tailwind CSS classes",
340+
"scope": "language-overridable"
341+
},
330342
"tailwindCSS.experimental.classRegex": {
331343
"type": "array",
332344
"scope": "language-overridable"

0 commit comments

Comments
 (0)