diff --git a/packages/tailwindcss-language-server/src/config.ts b/packages/tailwindcss-language-server/src/config.ts index 2e417fce..ee74975f 100644 --- a/packages/tailwindcss-language-server/src/config.ts +++ b/packages/tailwindcss-language-server/src/config.ts @@ -22,6 +22,7 @@ function getDefaultSettings(): Settings { rootFontSize: 16, lint: { cssConflict: 'warning', + deprecatedClass: 'warning', invalidApply: 'error', invalidScreen: 'error', invalidVariant: 'error', diff --git a/packages/tailwindcss-language-service/src/codeActions/codeActionProvider.ts b/packages/tailwindcss-language-service/src/codeActions/codeActionProvider.ts index 7ebf79cb..f7b55e70 100644 --- a/packages/tailwindcss-language-service/src/codeActions/codeActionProvider.ts +++ b/packages/tailwindcss-language-service/src/codeActions/codeActionProvider.ts @@ -13,6 +13,7 @@ import { isInvalidScreenDiagnostic, isInvalidVariantDiagnostic, isRecommendedVariantOrderDiagnostic, + isDeprecatedClassDiagnostic, } from '../diagnostics/types' import { flatten, dedupeBy } from '../util/array' import { provideCssConflictCodeActions } from './provideCssConflictCodeActions' @@ -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) } diff --git a/packages/tailwindcss-language-service/src/codeActions/provideSuggestionCodeActions.ts b/packages/tailwindcss-language-service/src/codeActions/provideSuggestionCodeActions.ts index a0201699..c8aedde3 100644 --- a/packages/tailwindcss-language-service/src/codeActions/provideSuggestionCodeActions.ts +++ b/packages/tailwindcss-language-service/src/codeActions/provideSuggestionCodeActions.ts @@ -1,6 +1,7 @@ import type { State } from '../util/state' import type { CodeActionParams, CodeAction } from 'vscode-languageserver' import type { + DeprecatedClassDiagnostic, InvalidConfigPathDiagnostic, InvalidTailwindDirectiveDiagnostic, InvalidScreenDiagnostic, @@ -12,6 +13,7 @@ export function provideSuggestionCodeActions( _state: State, params: CodeActionParams, diagnostic: + | DeprecatedClassDiagnostic | InvalidConfigPathDiagnostic | InvalidTailwindDirectiveDiagnostic | InvalidScreenDiagnostic diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts index 34c03b22..361ea485 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts @@ -8,11 +8,13 @@ import { getInvalidVariantDiagnostics } from './getInvalidVariantDiagnostics' import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnostics' import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics' import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics' +import { getDeprecatedClassDiagnostics } from './getDeprecatedClassDiagnostics' export async function doValidate( state: State, document: TextDocument, only: DiagnosticKind[] = [ + DiagnosticKind.Deprecation, DiagnosticKind.CssConflict, DiagnosticKind.InvalidApply, DiagnosticKind.InvalidScreen, @@ -26,6 +28,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) : []), diff --git a/packages/tailwindcss-language-service/src/diagnostics/getDeprecatedClassDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getDeprecatedClassDiagnostics.ts new file mode 100644 index 00000000..3fc7bd6c --- /dev/null +++ b/packages/tailwindcss-language-service/src/diagnostics/getDeprecatedClassDiagnostics.ts @@ -0,0 +1,46 @@ +import { joinWithAnd } from '../util/joinWithAnd' +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 { + // Only v4 projects can report deprecations + if (!state.v4) return [] + + let severity = settings.tailwindCSS.lint.deprecatedClass + if (severity === 'ignore') return [] + + let deprecatedClasses = new Set( + state.classList.filter(([_, meta]) => meta.deprecated ?? false).map(([className]) => className), + ) + + 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 (!deprecatedClasses.has(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 +} diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts index 115079a2..1fea2180 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/types.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts @@ -6,11 +6,24 @@ export enum DiagnosticKind { InvalidApply = 'invalidApply', InvalidScreen = 'invalidScreen', InvalidVariant = 'invalidVariant', + DeprecatedClass = 'deprecatedClass', InvalidConfigPath = 'invalidConfigPath', InvalidTailwindDirective = 'invalidTailwindDirective', 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 @@ -90,6 +103,7 @@ export function isRecommendedVariantOrderDiagnostic( } export type AugmentedDiagnostic = + | DeprecatedClassDiagnostic | CssConflictDiagnostic | InvalidApplyDiagnostic | InvalidScreenDiagnostic diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts index 50452dc0..90e84e8c 100644 --- a/packages/tailwindcss-language-service/src/util/state.ts +++ b/packages/tailwindcss-language-service/src/util/state.ts @@ -54,6 +54,7 @@ export type TailwindCssSettings = { colorDecorators: boolean lint: { cssConflict: DiagnosticSeveritySetting + deprecatedClass: DiagnosticSeveritySetting invalidApply: DiagnosticSeveritySetting invalidScreen: DiagnosticSeveritySetting invalidVariant: DiagnosticSeveritySetting @@ -91,6 +92,7 @@ export interface Variant { export interface ClassMetadata { color: culori.Color | KeywordColor | null modifiers?: string[] + deprecated?: boolean } export interface State { diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md index a401356d..84884e85 100644 --- a/packages/vscode-tailwindcss/CHANGELOG.md +++ b/packages/vscode-tailwindcss/CHANGELOG.md @@ -5,6 +5,7 @@ - Fix display of color swatches using new v4 oklch color palette ([#1073](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1073)) - Properly validate `theme(…)` function paths in v4 ([#1074](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1074)) - Show all potential class conflicts in v4 projects ([#1077](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1077)) +- Add support for detecting deprecated classes ([#1084](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1084)) ## 0.12.11 diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md index a37a06d7..c476ef5c 100644 --- a/packages/vscode-tailwindcss/README.md +++ b/packages/vscode-tailwindcss/README.md @@ -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`** diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 8c34a830..2f16948c 100644 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -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": [