From 2d1218d9338e0f4c01d73da1fbe22737c20eb64d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 10 Dec 2025 19:55:02 -0500 Subject: [PATCH 1/7] Cleanup --- .../src/css/ast.ts | 95 +------------------ .../src/css/css-context.ts | 32 +++++++ 2 files changed, 33 insertions(+), 94 deletions(-) create mode 100644 packages/tailwindcss-language-service/src/css/css-context.ts diff --git a/packages/tailwindcss-language-service/src/css/ast.ts b/packages/tailwindcss-language-service/src/css/ast.ts index ac95d204..11546640 100644 --- a/packages/tailwindcss-language-service/src/css/ast.ts +++ b/packages/tailwindcss-language-service/src/css/ast.ts @@ -1,6 +1,5 @@ -import { parseAtRule } from './parse' import type { SourceLocation } from './source' -import type { VisitContext } from '../util/walk' +import { parseAtRule } from './parse' const AT_SIGN = 0x40 @@ -117,95 +116,3 @@ export function atRoot(nodes: AstNode[]): AtRoot { nodes, } } - -export function cloneAstNode(node: T): T { - switch (node.kind) { - case 'rule': - return { - kind: node.kind, - selector: node.selector, - nodes: node.nodes.map(cloneAstNode), - src: node.src, - dst: node.dst, - } satisfies StyleRule as T - - case 'at-rule': - return { - kind: node.kind, - name: node.name, - params: node.params, - nodes: node.nodes.map(cloneAstNode), - src: node.src, - dst: node.dst, - } satisfies AtRule as T - - case 'at-root': - return { - kind: node.kind, - nodes: node.nodes.map(cloneAstNode), - src: node.src, - dst: node.dst, - } satisfies AtRoot as T - - case 'context': - return { - kind: node.kind, - context: { ...node.context }, - nodes: node.nodes.map(cloneAstNode), - src: node.src, - dst: node.dst, - } satisfies Context as T - - case 'declaration': - return { - kind: node.kind, - property: node.property, - value: node.value, - important: node.important, - src: node.src, - dst: node.dst, - } satisfies Declaration as T - - case 'comment': - return { - kind: node.kind, - value: node.value, - src: node.src, - dst: node.dst, - } satisfies Comment as T - - default: - node satisfies never - throw new Error(`Unknown node kind: ${(node as any).kind}`) - } -} - -export function cssContext( - ctx: VisitContext, -): VisitContext & { context: Record } { - return { - depth: ctx.depth, - get context() { - let context: Record = {} - for (let child of ctx.path()) { - if (child.kind === 'context') { - Object.assign(context, child.context) - } - } - - // Once computed, we never need to compute this again - Object.defineProperty(this, 'context', { value: context }) - return context - }, - get parent() { - let parent = (this.path().pop() as Extract) ?? null - - // Once computed, we never need to compute this again - Object.defineProperty(this, 'parent', { value: parent }) - return parent - }, - path() { - return ctx.path().filter((n) => n.kind !== 'context') - }, - } -} diff --git a/packages/tailwindcss-language-service/src/css/css-context.ts b/packages/tailwindcss-language-service/src/css/css-context.ts new file mode 100644 index 00000000..02ca1001 --- /dev/null +++ b/packages/tailwindcss-language-service/src/css/css-context.ts @@ -0,0 +1,32 @@ +import type { VisitContext } from '../util/walk' +import type { AstNode } from './ast' + +export function cssContext( + ctx: VisitContext, +): VisitContext & { context: Record } { + return { + depth: ctx.depth, + get context() { + let context: Record = {} + for (let child of ctx.path()) { + if (child.kind === 'context') { + Object.assign(context, child.context) + } + } + + // Once computed, we never need to compute this again + Object.defineProperty(this, 'context', { value: context }) + return context + }, + get parent() { + let parent = (this.path().pop() as Extract) ?? null + + // Once computed, we never need to compute this again + Object.defineProperty(this, 'parent', { value: parent }) + return parent + }, + path() { + return ctx.path().filter((n) => n.kind !== 'context') + }, + } +} From 88f6206b1f97be71f0d6b47a02c6b45b79a8c443 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 10 Dec 2025 20:14:53 -0500 Subject: [PATCH 2/7] Allow at-rules to have empty bodies --- packages/tailwindcss-language-service/src/css/ast.ts | 8 ++++---- .../src/css/clone-ast-node.ts | 2 +- .../src/css/from-postcss-ast.ts | 4 ++-- packages/tailwindcss-language-service/src/css/parse.ts | 8 +++++++- packages/tailwindcss-language-service/src/css/to-css.ts | 2 +- .../src/css/to-postcss-ast.ts | 8 ++++++-- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/tailwindcss-language-service/src/css/ast.ts b/packages/tailwindcss-language-service/src/css/ast.ts index 11546640..6d206c96 100644 --- a/packages/tailwindcss-language-service/src/css/ast.ts +++ b/packages/tailwindcss-language-service/src/css/ast.ts @@ -16,7 +16,7 @@ export type AtRule = { kind: 'at-rule' name: string params: string - nodes: AstNode[] + nodes: AstNode[] | null src?: SourceLocation dst?: SourceLocation @@ -69,7 +69,7 @@ export function styleRule(selector: string, nodes: AstNode[] = []): StyleRule { } } -export function atRule(name: string, params: string = '', nodes: AstNode[] = []): AtRule { +export function atRule(name: string, params: string = '', nodes: AstNode[] | null = []): AtRule { return { kind: 'at-rule', name, @@ -78,12 +78,12 @@ export function atRule(name: string, params: string = '', nodes: AstNode[] = []) } } -export function rule(selector: string, nodes: AstNode[] = []): StyleRule | AtRule { +export function rule(selector: string, nodes: AstNode[] | null = []): StyleRule | AtRule { if (selector.charCodeAt(0) === AT_SIGN) { return parseAtRule(selector, nodes) } - return styleRule(selector, nodes) + return styleRule(selector, nodes ?? []) } export function decl(property: string, value: string | undefined, important = false): Declaration { diff --git a/packages/tailwindcss-language-service/src/css/clone-ast-node.ts b/packages/tailwindcss-language-service/src/css/clone-ast-node.ts index ec62e482..7d0c4ea8 100644 --- a/packages/tailwindcss-language-service/src/css/clone-ast-node.ts +++ b/packages/tailwindcss-language-service/src/css/clone-ast-node.ts @@ -16,7 +16,7 @@ export function cloneAstNode(node: T): T { kind: node.kind, name: node.name, params: node.params, - nodes: node.nodes.map(cloneAstNode), + nodes: node.nodes?.map(cloneAstNode) ?? null, src: node.src, dst: node.dst, } satisfies AtRule as T diff --git a/packages/tailwindcss-language-service/src/css/from-postcss-ast.ts b/packages/tailwindcss-language-service/src/css/from-postcss-ast.ts index 9fb26ff7..04cc59ad 100644 --- a/packages/tailwindcss-language-service/src/css/from-postcss-ast.ts +++ b/packages/tailwindcss-language-service/src/css/from-postcss-ast.ts @@ -44,9 +44,9 @@ export function fromPostCSSAst(root: postcss.Root): AstNode[] { // AtRule else if (node.type === 'atrule') { - let astNode = atRule(`@${node.name}`, node.params) + let astNode = atRule(`@${node.name}`, node.params, node.nodes ? [] : null) astNode.src = toSource(node) - node.each((child) => transform(child, astNode.nodes)) + node.each((child) => transform(child, astNode.nodes!)) parent.push(astNode) } diff --git a/packages/tailwindcss-language-service/src/css/parse.ts b/packages/tailwindcss-language-service/src/css/parse.ts index d0f8ff22..4386f6d6 100644 --- a/packages/tailwindcss-language-service/src/css/parse.ts +++ b/packages/tailwindcss-language-service/src/css/parse.ts @@ -277,6 +277,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { } if (parent) { + parent.nodes ??= [] parent.nodes.push(declaration) } else { ast.push(declaration) @@ -303,6 +304,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { // At-rule is nested inside of a rule, attach it to the parent. if (parent) { + parent.nodes ??= [] parent.nodes.push(node) } @@ -343,6 +345,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { } if (parent) { + parent.nodes ??= [] parent.nodes.push(declaration) } else { ast.push(declaration) @@ -369,6 +372,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { // Attach the rule to the parent in case it's nested. if (parent) { + parent.nodes ??= [] parent.nodes.push(node) } @@ -421,6 +425,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { // At-rule is nested inside of a rule, attach it to the parent. if (parent) { + parent.nodes ??= [] parent.nodes.push(node) } @@ -460,6 +465,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { node.dst = [source, bufferStart, i] } + parent.nodes ??= [] parent.nodes.push(node) } } @@ -548,7 +554,7 @@ export function parse(input: string, opts?: ParseOptions): AstNode[] { return ast } -export function parseAtRule(buffer: string, nodes: AstNode[] = []): AtRule { +export function parseAtRule(buffer: string, nodes: AstNode[] | null = []): AtRule { let name = buffer let params = '' diff --git a/packages/tailwindcss-language-service/src/css/to-css.ts b/packages/tailwindcss-language-service/src/css/to-css.ts index 83e94742..cc532f67 100644 --- a/packages/tailwindcss-language-service/src/css/to-css.ts +++ b/packages/tailwindcss-language-service/src/css/to-css.ts @@ -91,7 +91,7 @@ export function toCss(ast: AstNode[], track?: boolean): string { // ```css // @layer base, components, utilities; // ``` - if (node.nodes.length === 0) { + if (!node.nodes) { let css = `${indent}${node.name} ${node.params};\n` if (track) { diff --git a/packages/tailwindcss-language-service/src/css/to-postcss-ast.ts b/packages/tailwindcss-language-service/src/css/to-postcss-ast.ts index 3495f323..697aaaad 100644 --- a/packages/tailwindcss-language-service/src/css/to-postcss-ast.ts +++ b/packages/tailwindcss-language-service/src/css/to-postcss-ast.ts @@ -85,11 +85,15 @@ export function toPostCSSAst(ast: AstNode[], source?: postcss.Source): postcss.R // AtRule else if (node.kind === 'at-rule') { - let astNode = postcss.atRule({ name: node.name.slice(1), params: node.params }) + let astNode = postcss.atRule({ + name: node.name.slice(1), + params: node.params, + nodes: node.nodes ? [] : undefined, + }) updateSource(astNode, node.src) astNode.raws.semicolon = true parent.append(astNode) - for (let child of node.nodes) { + for (let child of node.nodes ?? []) { transform(child, astNode) } } From dfdd15e95014ef198e1f89a36697c08abc851931 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 10 Dec 2025 20:21:36 -0500 Subject: [PATCH 3/7] fix --- packages/tailwindcss-language-service/src/css/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/tailwindcss-language-service/src/css/index.ts b/packages/tailwindcss-language-service/src/css/index.ts index c053e61b..3612b101 100644 --- a/packages/tailwindcss-language-service/src/css/index.ts +++ b/packages/tailwindcss-language-service/src/css/index.ts @@ -1,6 +1,7 @@ export * from './ast' export * from './source' export { parse } from './parse' +export { cloneAstNode } from './clone-ast-node' export { fromPostCSSAst } from './from-postcss-ast' export { toPostCSSAst } from './to-postcss-ast' export { toCss } from './to-css' From 46cfe1b9ccaa684d83cad1989ee4c6226cbdfd28 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 12 Dec 2025 10:08:37 -0500 Subject: [PATCH 4/7] Bump bundled Tailwind CSS version --- packages/tailwindcss-language-server/package.json | 2 +- .../tests/fixtures/v4/basic/package-lock.json | 8 ++++---- .../tests/fixtures/v4/basic/package.json | 2 +- .../tests/fixtures/v4/css-loading-js/package-lock.json | 8 ++++---- .../tests/fixtures/v4/css-loading-js/package.json | 2 +- .../tests/fixtures/v4/dependencies/package-lock.json | 8 ++++---- .../tests/fixtures/v4/dependencies/package.json | 2 +- .../fixtures/v4/invalid-import-order/package-lock.json | 8 ++++---- .../fixtures/v4/invalid-import-order/package.json | 2 +- .../tests/fixtures/v4/missing-files/package-lock.json | 8 ++++---- .../tests/fixtures/v4/missing-files/package.json | 2 +- .../tests/fixtures/v4/multi-config/package-lock.json | 8 ++++---- .../tests/fixtures/v4/multi-config/package.json | 2 +- .../tests/fixtures/v4/path-mappings/package-lock.json | 8 ++++---- .../tests/fixtures/v4/path-mappings/package.json | 2 +- .../tests/fixtures/v4/with-prefix/package-lock.json | 8 ++++---- .../tests/fixtures/v4/with-prefix/package.json | 2 +- .../tests/fixtures/v4/workspaces/package-lock.json | 8 ++++---- .../tests/fixtures/v4/workspaces/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 20 files changed, 51 insertions(+), 51 deletions(-) diff --git a/packages/tailwindcss-language-server/package.json b/packages/tailwindcss-language-server/package.json index 40b3e05a..9fded1e5 100644 --- a/packages/tailwindcss-language-server/package.json +++ b/packages/tailwindcss-language-server/package.json @@ -89,7 +89,7 @@ "rimraf": "3.0.2", "stack-trace": "0.0.10", "tailwindcss": "3.4.18", - "tailwindcss-v4": "npm:tailwindcss@4.1.17", + "tailwindcss-v4": "npm:tailwindcss@4.1.18", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.4", "tsconfig-paths": "^4.2.0", diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json index db44ced8..a4ae0b1f 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/basic/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json index 74935ce0..e072fed9 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/css-loading-js/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json index 3a6f2cc8..9ce001b7 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/dependencies/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json index ce9b94be..7bae87ca 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json index 6f89483b..56ae2345 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/missing-files/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json index f46fb487..73b3521d 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/multi-config/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json index 0580cf52..b1fa0b8e 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/path-mappings/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json index 6d75bfb8..d679cffd 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package-lock.json @@ -5,13 +5,13 @@ "packages": { "": { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json index f1752f59..b3c9f0cc 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/with-prefix/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json index 3dd6db07..8835c6b8 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json @@ -8,7 +8,7 @@ "packages/*" ], "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/@private/admin": { @@ -32,9 +32,9 @@ "link": true }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" }, "packages/admin": { diff --git a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json index 5a5b8df9..09a64442 100644 --- a/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json +++ b/packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package.json @@ -3,6 +3,6 @@ "packages/*" ], "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 216c758a..dc75d002 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -204,8 +204,8 @@ importers: specifier: 3.4.18 version: 3.4.18 tailwindcss-v4: - specifier: npm:tailwindcss@4.1.17 - version: tailwindcss@4.1.17 + specifier: npm:tailwindcss@4.1.18 + version: tailwindcss@4.1.18 tinyglobby: specifier: ^0.2.12 version: 0.2.12 @@ -2434,8 +2434,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.17: - resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} @@ -4674,7 +4674,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@4.1.17: {} + tailwindcss@4.1.18: {} tapable@2.2.1: {} From 414132371a070b6b31ebe5f59b4ff9e720d76110 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 12 Dec 2025 10:10:12 -0500 Subject: [PATCH 5/7] Update tests --- .../src/project-locator.test.ts | 16 ++++++++-------- .../tests/colors/colors.test.js | 4 ++-- .../tests/env/v4.test.js | 18 +++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts index 8e53c180..52998795 100644 --- a/packages/tailwindcss-language-server/src/project-locator.test.ts +++ b/packages/tailwindcss-language-server/src/project-locator.test.ts @@ -455,7 +455,7 @@ testLocator({ }, expected: [ { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/src/a/b/c/index.css', content: [], }, @@ -487,12 +487,12 @@ testLocator({ }, expected: [ { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/index.css', content: [], }, { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/src/vendor/c.css', content: [], }, @@ -519,12 +519,12 @@ testLocator({ }, expected: [ { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/src/app.css', content: [], }, { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/a/foo.css', content: [], }, @@ -554,12 +554,12 @@ testLocator({ }, expected: [ { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/src/app.css', content: [], }, { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/a/foo.css', content: [], }, @@ -581,7 +581,7 @@ testLocator({ }, expected: [ { - version: '4.1.17 (bundled)', + version: '4.1.18 (bundled)', config: '/src/app.css', content: [], }, diff --git a/packages/tailwindcss-language-server/tests/colors/colors.test.js b/packages/tailwindcss-language-server/tests/colors/colors.test.js index 92da0d58..f17e7ab2 100644 --- a/packages/tailwindcss-language-server/tests/colors/colors.test.js +++ b/packages/tailwindcss-language-server/tests/colors/colors.test.js @@ -349,7 +349,7 @@ defineTest({ expect(c.project).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) @@ -388,7 +388,7 @@ defineTest({ expect(c.project).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js index f054287a..df4ee18f 100644 --- a/packages/tailwindcss-language-server/tests/env/v4.test.js +++ b/packages/tailwindcss-language-server/tests/env/v4.test.js @@ -21,7 +21,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) @@ -137,7 +137,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) @@ -188,7 +188,7 @@ defineTest({ 'package.json': json` { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } `, @@ -205,7 +205,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: false, }, }) @@ -243,7 +243,7 @@ defineTest({ 'package.json': json` { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } `, @@ -270,7 +270,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: false, }, }) @@ -322,7 +322,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) @@ -354,7 +354,7 @@ defineTest({ 'package.json': json` { "dependencies": { - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } } `, @@ -831,7 +831,7 @@ defineTest({ expect(await client.project()).toMatchObject({ tailwind: { - version: '4.1.17', + version: '4.1.18', isDefaultVersion: true, }, }) From becb150f9b31c423e13c2997e9955b8ebea29a81 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 12 Dec 2025 10:10:18 -0500 Subject: [PATCH 6/7] Fix type errors --- .../src/completions/file-paths.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/tailwindcss-language-service/src/completions/file-paths.test.ts b/packages/tailwindcss-language-service/src/completions/file-paths.test.ts index ed521392..1be97f22 100644 --- a/packages/tailwindcss-language-service/src/completions/file-paths.test.ts +++ b/packages/tailwindcss-language-service/src/completions/file-paths.test.ts @@ -3,7 +3,7 @@ import { findFileDirective } from './file-paths' test('Detecting v3 directives that point to files', async () => { function find(text: string) { - return findFileDirective({ enabled: true, v4: false }, text) + return findFileDirective({ enabled: true, v4: false, features: [] }, text) } await expect(find('@config "./')).resolves.toEqual({ @@ -22,7 +22,7 @@ test('Detecting v3 directives that point to files', async () => { test('Detecting v4 directives that point to files', async () => { function find(text: string) { - return findFileDirective({ enabled: true, v4: true }, text) + return findFileDirective({ enabled: true, v4: true, features: [] }, text) } await expect(find('@config "./')).resolves.toEqual({ @@ -64,7 +64,7 @@ test('Detecting v4 directives that point to files', async () => { test('@source inline is ignored', async () => { function find(text: string) { - return findFileDirective({ enabled: true, v4: true }, text) + return findFileDirective({ enabled: true, v4: true, features: [] }, text) } await expect(find('@source inline("')).resolves.toEqual(null) From 2b66c57e67e2e2e7d71a4de60d5cfc9d19e37f17 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 12 Dec 2025 10:21:02 -0500 Subject: [PATCH 7/7] Tweak CSS printing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit How this prints will be dependent on Tailwind CSS version. Since we don’t know what version that is yet we’ll set up a simple toggle that we can flip after (eventually) threading version information through. --- packages/tailwindcss-language-service/src/css/to-css.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-service/src/css/to-css.ts b/packages/tailwindcss-language-service/src/css/to-css.ts index cc532f67..b2e6c869 100644 --- a/packages/tailwindcss-language-service/src/css/to-css.ts +++ b/packages/tailwindcss-language-service/src/css/to-css.ts @@ -3,6 +3,7 @@ import { Source } from './source' export function toCss(ast: AstNode[], track?: boolean): string { let pos = 0 + let supportsBodylessAtRules = false let source: Source = { file: null, @@ -84,6 +85,10 @@ export function toCss(ast: AstNode[], track?: boolean): string { // AtRule else if (node.kind === 'at-rule') { + let isBodyless = supportsBodylessAtRules + ? !node.nodes + : !node.nodes || node.nodes.length === 0 + // Print at-rules without nodes with a `;` instead of an empty block. // // E.g.: @@ -91,7 +96,7 @@ export function toCss(ast: AstNode[], track?: boolean): string { // ```css // @layer base, components, utilities; // ``` - if (!node.nodes) { + if (isBodyless) { let css = `${indent}${node.name} ${node.params};\n` if (track) {