From 36d363f3b47ffbfc362411dec8f9cde737147122 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:15:29 -0400 Subject: [PATCH 1/6] Warn when using blocklisted classes --- .../src/diagnostics/diagnosticsProvider.ts | 5 +++ .../getUsedBlocklistedClassDiagnostics.ts | 40 +++++++++++++++++++ .../src/diagnostics/types.ts | 12 ++++++ .../src/util/state.ts | 2 + packages/vscode-tailwindcss/README.md | 4 ++ packages/vscode-tailwindcss/package.json | 11 +++++ 6 files changed, 74 insertions(+) create mode 100644 packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts index 18994526c..075cea38e 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts @@ -9,6 +9,7 @@ import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnosti import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics' import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics' import { getInvalidSourceDiagnostics } from './getInvalidSourceDiagnostics' +import { getUsedBlocklistedClassDiagnostics } from './getUsedBlocklistedClassDiagnostics' export async function doValidate( state: State, @@ -22,6 +23,7 @@ export async function doValidate( DiagnosticKind.InvalidTailwindDirective, DiagnosticKind.InvalidSourceDirective, DiagnosticKind.RecommendedVariantOrder, + DiagnosticKind.UsedBlocklistedClass, ], ): Promise { const settings = await state.editor.getConfiguration(document.uri) @@ -52,6 +54,9 @@ export async function doValidate( ...(only.includes(DiagnosticKind.RecommendedVariantOrder) ? await getRecommendedVariantOrderDiagnostics(state, document, settings) : []), + ...(only.includes(DiagnosticKind.UsedBlocklistedClass) + ? await getUsedBlocklistedClassDiagnostics(state, document, settings) + : []), ] : [] } diff --git a/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts new file mode 100644 index 000000000..287c23038 --- /dev/null +++ b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts @@ -0,0 +1,40 @@ +import type { TextDocument } from 'vscode-languageserver-textdocument' +import type { State, Settings } from '../util/state' +import { type UsedBlocklistedClassDiagnostic, DiagnosticKind } from './types' +import { findClassListsInDocument, getClassNamesInClassList } from '../util/find' + +export async function getUsedBlocklistedClassDiagnostics( + state: State, + document: TextDocument, + settings: Settings, +): Promise { + if (!state.blocklist?.length) return [] + + let severity = settings.tailwindCSS.lint.usedBlocklistedClass + if (severity === 'ignore') return [] + + let blocklist = new Set(state.blocklist ?? []) + let diagnostics: UsedBlocklistedClassDiagnostic[] = [] + + let classLists = await findClassListsInDocument(state, document) + + for (let classList of classLists) { + let classNames = getClassNamesInClassList(classList, []) + + for (let className of classNames) { + if (!blocklist.has(className.className)) continue + + diagnostics.push({ + code: DiagnosticKind.UsedBlocklistedClass, + range: className.range, + severity: + severity === 'error' + ? 1 /* DiagnosticSeverity.Error */ + : 2 /* DiagnosticSeverity.Warning */, + message: `The class "${className.className}" will not be generated as it has been blocklisted`, + }) + } + } + + return diagnostics +} diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts index 7cb68a7ed..195924b51 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/types.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts @@ -10,6 +10,7 @@ export enum DiagnosticKind { InvalidTailwindDirective = 'invalidTailwindDirective', InvalidSourceDirective = 'invalidSourceDirective', RecommendedVariantOrder = 'recommendedVariantOrder', + UsedBlocklistedClass = 'UsedBlocklistedClass', } export type CssConflictDiagnostic = Diagnostic & { @@ -100,6 +101,16 @@ export function isRecommendedVariantOrderDiagnostic( return diagnostic.code === DiagnosticKind.RecommendedVariantOrder } +export type UsedBlocklistedClassDiagnostic = Diagnostic & { + code: DiagnosticKind.UsedBlocklistedClass +} + +export function isUsedBlocklistedClass( + diagnostic: AugmentedDiagnostic, +): diagnostic is UsedBlocklistedClassDiagnostic { + return diagnostic.code === DiagnosticKind.UsedBlocklistedClass +} + export type AugmentedDiagnostic = | CssConflictDiagnostic | InvalidApplyDiagnostic @@ -109,3 +120,4 @@ export type AugmentedDiagnostic = | InvalidTailwindDirectiveDiagnostic | InvalidSourceDirectiveDiagnostic | RecommendedVariantOrderDiagnostic + | UsedBlocklistedClassDiagnostic diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts index 8c819eb8f..754f2a497 100644 --- a/packages/tailwindcss-language-service/src/util/state.ts +++ b/packages/tailwindcss-language-service/src/util/state.ts @@ -65,6 +65,7 @@ export type TailwindCssSettings = { invalidTailwindDirective: DiagnosticSeveritySetting invalidSourceDirective: DiagnosticSeveritySetting recommendedVariantOrder: DiagnosticSeveritySetting + usedBlocklistedClass: DiagnosticSeveritySetting } experimental: { classRegex: string[] | [string, string][] @@ -203,6 +204,7 @@ export function getDefaultTailwindSettings(): Settings { invalidTailwindDirective: 'error', invalidSourceDirective: 'error', recommendedVariantOrder: 'warning', + usedBlocklistedClass: 'warning', }, showPixelEquivalents: true, includeLanguages: {}, diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md index efc937cc4..f74b14072 100644 --- a/packages/vscode-tailwindcss/README.md +++ b/packages/vscode-tailwindcss/README.md @@ -172,6 +172,10 @@ Class names on the same HTML element which apply the same CSS property or proper Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only). **Default: `warning`** +#### `tailwindCSS.lint.usedBlocklistedClass` + +Usage of class names that have been blocklisted via `@source not inline(…)`. **Default: `warning`** + ### `tailwindCSS.inspectPort` Enable the Node.js inspector agent for the language server and listen on the specified port. **Default: `null`** diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 26952a45e..d3d30911e 100644 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -305,6 +305,17 @@ "markdownDescription": "Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only)", "scope": "language-overridable" }, + "tailwindCSS.lint.usedBlocklistedClass": { + "type": "string", + "enum": [ + "ignore", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "Usage of class names that have been blocklisted via `@source not inline(…)`", + "scope": "language-overridable" + }, "tailwindCSS.experimental.classRegex": { "type": "array", "scope": "language-overridable" From ed94c5f7d6371ecfa89db9196c3772c076690830 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:15:45 -0400 Subject: [PATCH 2/6] Track blocked classes in v4 --- packages/tailwindcss-language-server/src/projects.ts | 6 +++++- .../src/util/v4/design-system.ts | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts index 25aacd5f0..04160569e 100644 --- a/packages/tailwindcss-language-server/src/projects.ts +++ b/packages/tailwindcss-language-server/src/projects.ts @@ -837,6 +837,7 @@ export async function createProjectService( ) state.designSystem = designSystem + state.blocklist = Array.from(designSystem.invalidCandidates ?? []) let deps = designSystem.dependencies() @@ -982,7 +983,9 @@ export async function createProjectService( if (typeof state.separator !== 'string') { state.separator = ':' } - state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : [] + if (!state.v4) { + state.blocklist = Array.isArray(state.config.blocklist) ? state.config.blocklist : [] + } delete state.config.blocklist if (state.v4) { @@ -1148,6 +1151,7 @@ export async function createProjectService( state.designSystem = designSystem state.classList = classList state.variants = getVariants(state) + state.blocklist = Array.from(designSystem.invalidCandidates ?? []) let deps = designSystem.dependencies() 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 20b7ff323..13c657c33 100644 --- a/packages/tailwindcss-language-service/src/util/v4/design-system.ts +++ b/packages/tailwindcss-language-service/src/util/v4/design-system.ts @@ -40,6 +40,7 @@ export interface DesignSystem { // Optional because it did not exist in earlier v4 alpha versions resolveThemeValue?(path: string, forceInline?: boolean): string | undefined + invalidCandidates?: Set } export interface DesignSystem { From 4e6860039202be628f6c682bc4b20eb1d36dff2d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:15:56 -0400 Subject: [PATCH 3/6] Limit blocked class warning to v4 projects --- .../src/diagnostics/getUsedBlocklistedClassDiagnostics.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts index 287c23038..07f1084c5 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getUsedBlocklistedClassDiagnostics.ts @@ -8,6 +8,7 @@ export async function getUsedBlocklistedClassDiagnostics( document: TextDocument, settings: Settings, ): Promise { + if (!state.v4) return [] if (!state.blocklist?.length) return [] let severity = settings.tailwindCSS.lint.usedBlocklistedClass From bb4bd756b37e28d9513fcb38344a2163f3213b5a Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:23:31 -0400 Subject: [PATCH 4/6] wip --- packages/tailwindcss-language-service/src/diagnostics/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts index 195924b51..f022503aa 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/types.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts @@ -10,7 +10,7 @@ export enum DiagnosticKind { InvalidTailwindDirective = 'invalidTailwindDirective', InvalidSourceDirective = 'invalidSourceDirective', RecommendedVariantOrder = 'recommendedVariantOrder', - UsedBlocklistedClass = 'UsedBlocklistedClass', + UsedBlocklistedClass = 'usedBlocklistedClass', } export type CssConflictDiagnostic = Diagnostic & { From eb2cc44dd4ea184e3ca1e43f746507d4c235798f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:23:34 -0400 Subject: [PATCH 5/6] Add test --- .../tests/diagnostics/diagnostics.test.js | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js index 56fe9f7ff..afda88373 100644 --- a/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js +++ b/packages/tailwindcss-language-server/tests/diagnostics/diagnostics.test.js @@ -1,6 +1,8 @@ +import * as fs from 'node:fs/promises' import { expect, test } from 'vitest' import { withFixture } from '../common' -import * as fs from 'node:fs/promises' +import { css, defineTest } from '../../src/testing' +import { createClient } from '../utils/client' withFixture('basic', (c) => { function testFixture(fixture) { @@ -383,3 +385,43 @@ withFixture('v4/basic', (c) => { ], }) }) + +defineTest({ + name: 'Shows warning when using blocklisted classes', + fs: { + 'app.css': css` + @import 'tailwindcss'; + @source not inline("{,hover:}flex"); + `, + }, + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let doc = await client.open({ + lang: 'html', + text: '
', + }) + + let diagnostics = await doc.diagnostics() + + expect(diagnostics).toEqual([ + { + code: 'usedBlocklistedClass', + message: 'The class "flex" will not be generated as it has been blocklisted', + range: { + start: { line: 0, character: 12 }, + end: { line: 0, character: 16 }, + }, + severity: 2, + }, + { + code: 'usedBlocklistedClass', + message: 'The class "hover:flex" will not be generated as it has been blocklisted', + range: { + start: { line: 0, character: 27 }, + end: { line: 0, character: 37 }, + }, + severity: 2, + }, + ]) + }, +}) From bc82f1c31024026c7c210c3dfce020aeb784ef31 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 11 Apr 2025 19:24:41 -0400 Subject: [PATCH 6/6] Update changelog --- packages/vscode-tailwindcss/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md index 17f07afc2..4c9240f60 100644 --- a/packages/vscode-tailwindcss/CHANGELOG.md +++ b/packages/vscode-tailwindcss/CHANGELOG.md @@ -2,7 +2,7 @@ ## Prerelease -- Nothing yet! +- Warn when using a blocklisted class in v4 ([#1310](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1310)) # 0.14.15