From 469d3b3a17eb49bd3fd5cfc428a405244fb25374 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 4 Jun 2025 11:17:25 -0400 Subject: [PATCH 1/2] Fix infinite loop when resolving completion details --- .../src/util/rewriting/index.test.ts | 37 +++++++++++++++++++ .../src/util/rewriting/inline-theme-values.ts | 12 ++++++ 2 files changed, 49 insertions(+) diff --git a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts index de51a2b7..3a150a01 100644 --- a/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts +++ b/packages/tailwindcss-language-service/src/util/rewriting/index.test.ts @@ -2,6 +2,7 @@ import { expect, test } from 'vitest' import { addThemeValues, evaluateExpression, + inlineThemeValues, replaceCssCalc, replaceCssVarsWithFallbacks, } from './index' @@ -124,6 +125,42 @@ test('recursive theme replacements', () => { ) }) +test('recursive theme replacements (inlined)', () => { + let map = new Map([ + ['--color-a', 'var(--color-a)'], + ['--color-b', 'rgb(var(--color-b))'], + ['--color-c', 'rgb(var(--channel) var(--channel) var(--channel))'], + ['--channel', '255'], + + ['--color-d', 'rgb(var(--indirect) var(--indirect) var(--indirect))'], + ['--indirect', 'var(--channel)'], + ['--channel', '255'], + + ['--mutual-a', 'calc(var(--mutual-b) * 1)'], + ['--mutual-b', 'calc(var(--mutual-a) + 1)'], + ]) + + let state: State = { + enabled: true, + designSystem: { + theme: { prefix: null } as any, + resolveThemeValue: (name) => map.get(name) ?? null, + } as DesignSystem, + } + + expect(inlineThemeValues('var(--color-a)', state)).toBe('var(--color-a)') + expect(inlineThemeValues('var(--color-b)', state)).toBe('rgb(var(--color-b))') + expect(inlineThemeValues('var(--color-c)', state)).toBe('rgb(255 255 255)') + + // This one is wrong but fixing it without breaking the infinite recursion guard is complex + expect(inlineThemeValues('var(--color-d)', state)).toBe( + 'rgb(255 var(--indirect) var(--indirect))', + ) + + expect(inlineThemeValues('var(--mutual-a)', state)).toBe('calc(calc(var(--mutual-a) + 1) * 1)') + expect(inlineThemeValues('var(--mutual-b)', state)).toBe('calc(calc(var(--mutual-b) * 1) + 1)') +}) + test('Evaluating CSS calc expressions', () => { expect(replaceCssCalc('calc(1px + 1px)', (node) => evaluateExpression(node.value))).toBe('2px') expect(replaceCssCalc('calc(1px * 4)', (node) => evaluateExpression(node.value))).toBe('4px') diff --git a/packages/tailwindcss-language-service/src/util/rewriting/inline-theme-values.ts b/packages/tailwindcss-language-service/src/util/rewriting/inline-theme-values.ts index 5c1eb8ed..26349f68 100644 --- a/packages/tailwindcss-language-service/src/util/rewriting/inline-theme-values.ts +++ b/packages/tailwindcss-language-service/src/util/rewriting/inline-theme-values.ts @@ -7,13 +7,20 @@ import { replaceCssVars, replaceCssCalc } from './replacements' export function inlineThemeValues(css: string, state: State): string { if (!state.designSystem) return css + let seen = new Set() + css = replaceCssCalc(css, (expr) => { let inlined = replaceCssVars(expr.value, { replace({ name, fallback }) { if (!name.startsWith('--')) return null + // TODO: This isn't quite right as we might skip expanding a variable + // that should be expanded + if (seen.has(name)) return null + let value = resolveVariableValue(state.designSystem, name) if (value === null) return fallback + if (value.includes('var(')) seen.add(name) return value }, @@ -26,8 +33,13 @@ export function inlineThemeValues(css: string, state: State): string { replace({ name, fallback }) { if (!name.startsWith('--')) return null + // TODO: This isn't quite right as we might skip expanding a variable + // that should be expanded + if (seen.has(name)) return null + let value = resolveVariableValue(state.designSystem, name) if (value === null) return fallback + if (value.includes('var(')) seen.add(name) return value }, From 529ee014e16798bb4bb6d19739d4daa46ce7572e Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 4 Jun 2025 11:18:24 -0400 Subject: [PATCH 2/2] Update changelog --- packages/vscode-tailwindcss/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md index e3bfc615..5cdd4db7 100644 --- a/packages/vscode-tailwindcss/CHANGELOG.md +++ b/packages/vscode-tailwindcss/CHANGELOG.md @@ -3,6 +3,7 @@ ## Prerelease - Bump bundled CSS language service ([#1395](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1395)) +- Fix infinite loop when resolving completion details with recursive theme keys ([#1400](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1400)) ## 0.14.20