From b4a20af4d006f39890737f1818dab319ed322491 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 10 Jan 2025 15:07:23 +0100 Subject: [PATCH] Improve upgrade tool to pretty print `--spacing(2)` (#15596) This PR improves the upgrade tool to make sure that newly upgraded `--spacing(2)` CSS functions is pretty printed to prevent unambiguous looking classes (even though it compiles correctly). If you have a class such as `m-[calc(100dvh-theme(spacing.2))]`, then we used to convert it to `m-[calc(100dvh-calc(var(--spacing)*2))]`. But recently we introduced the `--spacing(2)` CSS function which means that the output now looks like this instead: `m-[calc(100dvh---spacing(2))]`. The triple `-` is valid because the first `-` is the minus sign, the next two `-` characters are from the function. One solution is to introduce spaces via underscores: ``` m-[calc(100dvh_-_--spacing(2))] ``` But a simpler solution, is to wrap the `--spacing(2)` in parens to remove the underscores and improve the readability of the `---` characters. ``` m-[calc(100dvh-(--spacing(2)))] ``` --- CHANGELOG.md | 1 + .../template/codemods/theme-to-var.test.ts | 7 ++++ .../src/template/codemods/theme-to-var.ts | 33 ++++++++++++++++++- .../tailwindcss/src/css-functions.test.ts | 4 ++- 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1acd15b57212..96d38c1b1b1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Add missing `main` and `browser` fields for `@tailwindcss/browser` ([#15594](https://github.com/tailwindlabs/tailwindcss/pull/15594)) +- _Upgrade (experimental)_: Pretty print `--spacing(…)` to prevent ambiguity ([#15596](https://github.com/tailwindlabs/tailwindcss/pull/15596)) ## [4.0.0-beta.9] - 2025-01-09 diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts b/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts index 2ff0740107f3..c5131fc29499 100644 --- a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts +++ b/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.test.ts @@ -22,6 +22,13 @@ test.each([ ['bg-[theme(colors.red.500)]', 'bg-(--color-red-500)'], // Arbitrary value ['bg-[size:theme(spacing.4)]', 'bg-[size:--spacing(4)]'], // Arbitrary value + data type hint + // Pretty print CSS functions preceded by an operator to prevent consecutive + // operator characters. + ['w-[calc(100dvh-theme(spacing.2))]', 'w-[calc(100dvh-(--spacing(2)))]'], + ['w-[calc(100dvh+theme(spacing.2))]', 'w-[calc(100dvh+(--spacing(2)))]'], + ['w-[calc(100dvh/theme(spacing.2))]', 'w-[calc(100dvh/(--spacing(2)))]'], + ['w-[calc(100dvh*theme(spacing.2))]', 'w-[calc(100dvh*(--spacing(2)))]'], + // Convert to `var(…)` if we can resolve the path, but keep fallback values ['bg-[theme(colors.red.500,red)]', 'bg-(--color-red-500,red)'], diff --git a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.ts b/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.ts index 128b5e8b8386..6248d43473fb 100644 --- a/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.ts +++ b/packages/@tailwindcss-upgrade/src/template/codemods/theme-to-var.ts @@ -237,7 +237,7 @@ function substituteFunctionsInValue( ast: ValueParser.ValueAstNode[], handle: (value: string, fallback?: string) => string | null, ) { - ValueParser.walk(ast, (node, { replaceWith }) => { + ValueParser.walk(ast, (node, { parent, replaceWith }) => { if (node.kind === 'function' && node.value === 'theme') { if (node.nodes.length < 1) return @@ -275,6 +275,37 @@ function substituteFunctionsInValue( fallbackValues.length > 0 ? handle(path, ValueParser.toCss(fallbackValues)) : handle(path) if (replacement === null) return + if (parent) { + let idx = parent.nodes.indexOf(node) - 1 + while (idx !== -1) { + let previous = parent.nodes[idx] + // Skip the space separator + if (previous.kind === 'separator' && previous.value.trim() === '') { + idx -= 1 + continue + } + + // If the previous node is a word and contains an operator, we need to + // wrap the replacement in parentheses to make the output less + // ambiguous. + // + // Input: + // - `calc(100dvh-theme(spacing.2))` + // + // Output: + // - `calc(100dvh-(--spacing(2)))` + // + // Not: + // -`calc(100dvh---spacing(2))` + // + if (/^[-+*/]$/.test(previous.value.trim())) { + replacement = `(${replacement})` + } + + break + } + } + replaceWith(ValueParser.parse(replacement)) } }) diff --git a/packages/tailwindcss/src/css-functions.test.ts b/packages/tailwindcss/src/css-functions.test.ts index c91f5fee7787..128053f46cbf 100644 --- a/packages/tailwindcss/src/css-functions.test.ts +++ b/packages/tailwindcss/src/css-functions.test.ts @@ -177,7 +177,9 @@ describe('--theme(…)', () => { color: --theme(colors.red.500); } `), - ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: The --theme(…) function can only be used with CSS variables from your theme.]`) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `[Error: The --theme(…) function can only be used with CSS variables from your theme.]`, + ) }) })