mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Allow newlines and tabs in the argument list of the theme() function (#14917)
We noticed an issue that the `theme()` function wourld not properly
parse in CSS if you split the argument list over multiple lines. This is
fixed by treating `\n` and `\t` the same as space:
```css
.custom-font {
font-family: theme(
fontFamily.unknown,
Helvetica Neue,
Helvetica,
sans-serif
);
}
```
## Test plan
Added tests, but also tried it in the Vite example:
<img width="1995" alt="Screenshot 2024-11-08 at 13 46 09"
src="https://github.com/user-attachments/assets/f9bf94b0-3f9b-4334-8911-9190987e2df5">
This commit is contained in:
parent
c1c94d8d7a
commit
aaa32e23e2
@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Ensure adjacent rules are merged together after handling nesting when generating optimized CSS ([#14873](https://github.com/tailwindlabs/tailwindcss/pull/14873))
|
||||
- Rebase `url()` inside imported CSS files when using Vite ([#14877](https://github.com/tailwindlabs/tailwindcss/pull/14877))
|
||||
- Ensure that CSS transforms from other Vite plugins correctly work in full builds (e.g. `:deep()` in Vue) ([#14871](https://github.com/tailwindlabs/tailwindcss/pull/14871))
|
||||
- Ensure the CSS `theme()` function handles newlines and tabs in its arguments list ([#14917](https://github.com/tailwindlabs/tailwindcss/pull/14917))
|
||||
- Don't unset keys like `--inset-shadow-*` when unsetting keys like `--inset-*` ([#14906](https://github.com/tailwindlabs/tailwindcss/pull/14906))
|
||||
- Ensure spacing utilities with no value (e.g. `px` or `translate-y`) don't generate CSS ([#14911](https://github.com/tailwindlabs/tailwindcss/pull/14911))
|
||||
- _Upgrade (experimental)_: Install `@tailwindcss/postcss` next to `tailwindcss` ([#14830](https://github.com/tailwindlabs/tailwindcss/pull/14830))
|
||||
|
||||
@ -134,6 +134,11 @@ function substituteFunctionsInValue(
|
||||
if (node.kind === 'function' && node.value === 'theme') {
|
||||
if (node.nodes.length < 1) return
|
||||
|
||||
// Ignore whitespace before the first argument
|
||||
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
|
||||
node.nodes.shift()
|
||||
}
|
||||
|
||||
let pathNode = node.nodes[0]
|
||||
if (pathNode.kind !== 'word') return
|
||||
|
||||
|
||||
@ -236,6 +236,11 @@ function substituteFunctionsInValue(
|
||||
if (node.kind === 'function' && node.value === 'theme') {
|
||||
if (node.nodes.length < 1) return
|
||||
|
||||
// Ignore whitespace before the first argument
|
||||
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
|
||||
node.nodes.shift()
|
||||
}
|
||||
|
||||
let pathNode = node.nodes[0]
|
||||
if (pathNode.kind !== 'word') return
|
||||
|
||||
|
||||
@ -473,6 +473,26 @@ describe('theme function', () => {
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('theme(\n\tfontFamily.unknown,\n\tHelvetica Neue,\n\tHelvetica,\n\tsans-serif\n)', async () => {
|
||||
expect(
|
||||
// prettier-ignore
|
||||
await compileCss(css`
|
||||
.fam {
|
||||
font-family: theme(
|
||||
fontFamily.unknown,
|
||||
Helvetica Neue,
|
||||
Helvetica,
|
||||
sans-serif
|
||||
);
|
||||
}
|
||||
`),
|
||||
).toMatchInlineSnapshot(`
|
||||
".fam {
|
||||
font-family: Helvetica Neue, Helvetica, sans-serif;
|
||||
}"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('recursive theme()', () => {
|
||||
|
||||
@ -42,6 +42,11 @@ export function substituteFunctionsInValue(
|
||||
)
|
||||
}
|
||||
|
||||
// Ignore whitespace before the first argument
|
||||
if (node.nodes[0].kind === 'separator' && node.nodes[0].value.trim() === '') {
|
||||
node.nodes.shift()
|
||||
}
|
||||
|
||||
let pathNode = node.nodes[0]
|
||||
if (pathNode.kind !== 'word') {
|
||||
throw new Error(
|
||||
|
||||
@ -52,6 +52,22 @@ describe('parse', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('should parse a function with multiple arguments across lines', () => {
|
||||
expect(parse('theme(\n\tfoo,\n\tbar\n)')).toEqual([
|
||||
{
|
||||
kind: 'function',
|
||||
value: 'theme',
|
||||
nodes: [
|
||||
{ kind: 'separator', value: '\n\t' },
|
||||
{ kind: 'word', value: 'foo' },
|
||||
{ kind: 'separator', value: ',\n\t' },
|
||||
{ kind: 'word', value: 'bar' },
|
||||
{ kind: 'separator', value: '\n' },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('should parse a function with nested arguments', () => {
|
||||
expect(parse('theme(foo, theme(bar))')).toEqual([
|
||||
{
|
||||
|
||||
@ -109,13 +109,15 @@ const CLOSE_PAREN = 0x29
|
||||
const COLON = 0x3a
|
||||
const COMMA = 0x2c
|
||||
const DOUBLE_QUOTE = 0x22
|
||||
const EQUALS = 0x3d
|
||||
const GREATER_THAN = 0x3e
|
||||
const LESS_THAN = 0x3c
|
||||
const NEWLINE = 0x0a
|
||||
const OPEN_PAREN = 0x28
|
||||
const SINGLE_QUOTE = 0x27
|
||||
const SPACE = 0x20
|
||||
const LESS_THAN = 0x3c
|
||||
const GREATER_THAN = 0x3e
|
||||
const EQUALS = 0x3d
|
||||
const SLASH = 0x2f
|
||||
const SPACE = 0x20
|
||||
const TAB = 0x09
|
||||
|
||||
export function parse(input: string) {
|
||||
input = input.replaceAll('\r\n', '\n')
|
||||
@ -144,11 +146,13 @@ export function parse(input: string) {
|
||||
// ```
|
||||
case COLON:
|
||||
case COMMA:
|
||||
case SPACE:
|
||||
case SLASH:
|
||||
case LESS_THAN:
|
||||
case EQUALS:
|
||||
case GREATER_THAN:
|
||||
case EQUALS: {
|
||||
case LESS_THAN:
|
||||
case NEWLINE:
|
||||
case SLASH:
|
||||
case SPACE:
|
||||
case TAB: {
|
||||
// 1. Handle everything before the separator as a word
|
||||
// Handle everything before the closing paren as a word
|
||||
if (buffer.length > 0) {
|
||||
@ -169,11 +173,13 @@ export function parse(input: string) {
|
||||
if (
|
||||
peekChar !== COLON &&
|
||||
peekChar !== COMMA &&
|
||||
peekChar !== SPACE &&
|
||||
peekChar !== SLASH &&
|
||||
peekChar !== LESS_THAN &&
|
||||
peekChar !== EQUALS &&
|
||||
peekChar !== GREATER_THAN &&
|
||||
peekChar !== EQUALS
|
||||
peekChar !== LESS_THAN &&
|
||||
peekChar !== NEWLINE &&
|
||||
peekChar !== SLASH &&
|
||||
peekChar !== SPACE &&
|
||||
peekChar !== TAB
|
||||
) {
|
||||
break
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user