Skip to content

Support detection of deprecated classes #1084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/tailwindcss-language-server/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function getDefaultSettings(): Settings {
rootFontSize: 16,
lint: {
cssConflict: 'warning',
deprecatedClass: 'warning',
invalidApply: 'error',
invalidScreen: 'error',
invalidVariant: 'error',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
isInvalidScreenDiagnostic,
isInvalidVariantDiagnostic,
isRecommendedVariantOrderDiagnostic,
isDeprecatedClassDiagnostic,
} from '../diagnostics/types'
import { flatten, dedupeBy } from '../util/array'
import { provideCssConflictCodeActions } from './provideCssConflictCodeActions'
Expand Down Expand Up @@ -74,7 +75,8 @@ export async function doCodeActions(
isInvalidTailwindDirectiveDiagnostic(diagnostic) ||
isInvalidScreenDiagnostic(diagnostic) ||
isInvalidVariantDiagnostic(diagnostic) ||
isRecommendedVariantOrderDiagnostic(diagnostic)
isRecommendedVariantOrderDiagnostic(diagnostic) ||
isDeprecatedClassDiagnostic(diagnostic)
) {
return provideSuggestionCodeActions(state, params, diagnostic)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { State } from '../util/state'
import type { CodeActionParams, CodeAction } from 'vscode-languageserver'
import type {
DeprecatedClassDiagnostic,
InvalidConfigPathDiagnostic,
InvalidTailwindDirectiveDiagnostic,
InvalidScreenDiagnostic,
Expand All @@ -12,6 +13,7 @@ export function provideSuggestionCodeActions(
_state: State,
params: CodeActionParams,
diagnostic:
| DeprecatedClassDiagnostic
| InvalidConfigPathDiagnostic
| InvalidTailwindDirectiveDiagnostic
| InvalidScreenDiagnostic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnosti
import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics'
import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics'
import { getInvalidSourceDiagnostics } from './getInvalidSourceDiagnostics'
import { getDeprecatedClassDiagnostics } from './getDeprecatedClassDiagnostics'

export async function doValidate(
state: State,
document: TextDocument,
only: DiagnosticKind[] = [
DiagnosticKind.Deprecation,
DiagnosticKind.CssConflict,
DiagnosticKind.InvalidApply,
DiagnosticKind.InvalidScreen,
Expand All @@ -28,6 +30,9 @@ export async function doValidate(

return settings.tailwindCSS.validate
? [
...(only.includes(DiagnosticKind.Deprecation)
? await getDeprecatedClassDiagnostics(state, document, settings)
: []),
...(only.includes(DiagnosticKind.CssConflict)
? await getCssConflictDiagnostics(state, document, settings)
: []),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { State, Settings } from '../util/state'
import { type DeprecatedClassDiagnostic, DiagnosticKind } from './types'
import { findClassListsInDocument, getClassNamesInClassList } from '../util/find'
import type { TextDocument } from 'vscode-languageserver-textdocument'

export async function getDeprecatedClassDiagnostics(
state: State,
document: TextDocument,
settings: Settings,
): Promise<DeprecatedClassDiagnostic[]> {
// Only v4 projects can report deprecations
if (!state.v4) return []

// This is an earlier v4 version that does not support class deprecations
if (!state.designSystem.classMetadata) return []

let severity = settings.tailwindCSS.lint.deprecatedClass
if (severity === 'ignore') return []

// Fill in the list of statically known deprecated classes
let deprecations = new Map<string, boolean>(
state.classList.map(([className, meta]) => [className, meta.deprecated ?? false]),
)

function isDeprecated(className: string) {
if (deprecations.has(className)) {
return deprecations.get(className)
}

let metadata = state.designSystem.classMetadata([className])[0]
let deprecated = metadata?.deprecated ?? false

deprecations.set(className, deprecated)

return deprecated
}

let diagnostics: DeprecatedClassDiagnostic[] = []
let classLists = await findClassListsInDocument(state, document)

for (let classList of classLists) {
let classNames = getClassNamesInClassList(classList, state.blocklist)

for (let className of classNames) {
if (!isDeprecated(className.className)) continue

diagnostics.push({
code: DiagnosticKind.DeprecatedClass,
className,
range: className.range,
severity:
severity === 'error'
? 1 /* DiagnosticSeverity.Error */
: 2 /* DiagnosticSeverity.Warning */,
message: `'${className.className}' is deprecated.`,
suggestions: [],
})
}
}

return diagnostics
}
14 changes: 14 additions & 0 deletions packages/tailwindcss-language-service/src/diagnostics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,25 @@ export enum DiagnosticKind {
InvalidApply = 'invalidApply',
InvalidScreen = 'invalidScreen',
InvalidVariant = 'invalidVariant',
DeprecatedClass = 'deprecatedClass',
InvalidConfigPath = 'invalidConfigPath',
InvalidTailwindDirective = 'invalidTailwindDirective',
InvalidSourceDirective = 'invalidSourceDirective',
RecommendedVariantOrder = 'recommendedVariantOrder',
}

export type DeprecatedClassDiagnostic = Diagnostic & {
code: DiagnosticKind.DeprecatedClass
className: DocumentClassName
suggestions: string[]
}

export function isDeprecatedClassDiagnostic(
diagnostic: AugmentedDiagnostic,
): diagnostic is DeprecatedClassDiagnostic {
return diagnostic.code === DiagnosticKind.DeprecatedClass
}

export type CssConflictDiagnostic = Diagnostic & {
code: DiagnosticKind.CssConflict
className: DocumentClassName
Expand Down Expand Up @@ -101,6 +114,7 @@ export function isRecommendedVariantOrderDiagnostic(
}

export type AugmentedDiagnostic =
| DeprecatedClassDiagnostic
| CssConflictDiagnostic
| InvalidApplyDiagnostic
| InvalidScreenDiagnostic
Expand Down
2 changes: 2 additions & 0 deletions packages/tailwindcss-language-service/src/util/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export type TailwindCssSettings = {
colorDecorators: boolean
lint: {
cssConflict: DiagnosticSeveritySetting
deprecatedClass: DiagnosticSeveritySetting
invalidApply: DiagnosticSeveritySetting
invalidScreen: DiagnosticSeveritySetting
invalidVariant: DiagnosticSeveritySetting
Expand Down Expand Up @@ -92,6 +93,7 @@ export interface Variant {
export interface ClassMetadata {
color: culori.Color | KeywordColor | null
modifiers?: string[]
deprecated?: boolean
}

export type ClassEntry = [string, ClassMetadata]
Expand Down
12 changes: 12 additions & 0 deletions packages/tailwindcss-language-service/src/util/v4/design-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ export interface Theme {

export interface ClassMetadata {
modifiers: string[]
deprecated?: boolean
}

export interface VariantMetadata {
deprecated?: boolean
}

export interface ThemeMetadata {
deprecated?: boolean
}

export type ClassEntry = [string, ClassMetadata]
Expand Down Expand Up @@ -40,6 +49,9 @@ export interface DesignSystem {

// Optional because it did not exist in earlier v4 alpha versions
resolveThemeValue?(path: string): string | undefined
classMetadata?(classes: string[]): (ClassMetadata | null)[]
variantMetadata?(variants: string[]): (VariantMetadata | null)[]
themeMetadata?(keys: string[]): (ThemeMetadata | null)[]
}

export interface DesignSystem {
Expand Down
1 change: 1 addition & 0 deletions packages/vscode-tailwindcss/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Show source diagnostics when imports contain a layer ([#1204](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1204))
- Only detect project roots in v4 when using certain CSS features ([#1205](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1205))
- Update Tailwind CSS v4 version to v4.0.6 ([#1207](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1207))
- Add support for detecting deprecated classes ([#1084](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1084))

## 0.14.4

Expand Down
4 changes: 4 additions & 0 deletions packages/vscode-tailwindcss/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ Unknown or invalid path used with the [`theme` helper](https://tailwindcss.com/d

Class names on the same HTML element which apply the same CSS property or properties. **Default: `warning`**

#### `tailwindCSS.lint.deprecatedClass`

Use of a deprecated class. **Default: `warning`**

#### `tailwindCSS.lint.recommendedVariantOrder`

Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only). **Default: `warning`**
Expand Down
11 changes: 11 additions & 0 deletions packages/vscode-tailwindcss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,17 @@
"markdownDescription": "Class names on the same HTML element which apply the same CSS property or properties",
"scope": "language-overridable"
},
"tailwindCSS.lint.deprecatedClass": {
"type": "string",
"enum": [
"ignore",
"warning",
"error"
],
"default": "warning",
"markdownDescription": "Use of a deprecated utility class",
"scope": "language-overridable"
},
"tailwindCSS.lint.invalidApply": {
"type": "string",
"enum": [
Expand Down