Skip to content

Commit

Permalink
Improve upgrade tool to pretty print --spacing(2) (#15596)
Browse files Browse the repository at this point in the history
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)))]
```
  • Loading branch information
RobinMalfait authored Jan 10, 2025
1 parent 27f8bab commit b4a20af
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)'],

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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))
}
})
Expand Down
4 changes: 3 additions & 1 deletion packages/tailwindcss/src/css-functions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.]`,
)
})
})

Expand Down

0 comments on commit b4a20af

Please sign in to comment.