diff --git a/packages/tailwindcss-language-server/src/config.ts b/packages/tailwindcss-language-server/src/config.ts index 90fb3207..fd3a9b76 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 18994526..4c7d0eb0 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts @@ -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, @@ -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) : []), 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..d753641e --- /dev/null +++ b/packages/tailwindcss-language-service/src/diagnostics/getDeprecatedClassDiagnostics.ts @@ -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 { + // 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( + 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 +} diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts index 7cb68a7e..640ca0ff 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/types.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts @@ -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 @@ -101,6 +114,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 621add49..b20727fc 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 @@ -92,6 +93,7 @@ export interface Variant { export interface ClassMetadata { color: culori.Color | KeywordColor | null modifiers?: string[] + deprecated?: boolean } export type ClassEntry = [string, ClassMetadata] diff --git a/packages/tailwindcss-language-service/src/util/v4/design-system.ts b/packages/tailwindcss-language-service/src/util/v4/design-system.ts index cce64d4b..b6a86cfb 100644 --- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts +++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts @@ -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] @@ -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 { diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md index 8896fc88..82548174 100644 --- a/packages/vscode-tailwindcss/CHANGELOG.md +++ b/packages/vscode-tailwindcss/CHANGELOG.md @@ -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 diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md index 62165308..2befb4f5 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 52ad3ebd..9b4ec038 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": [