Skip to content

Commit 336a7de

Browse files
Fix recursion in legacy CSS theme(…) function
1 parent 2d13998 commit 336a7de

File tree

3 files changed

+45
-1
lines changed

3 files changed

+45
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Fix infinite loop when `theme()` function is called recursively inside `@theme` blocks
1113

1214
## [4.1.6] - 2025-05-09
1315

packages/tailwindcss/src/css-functions.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,27 @@ describe('--theme(…)', () => {
433433
}"
434434
`)
435435
})
436+
437+
test('--theme(…) prevents infinite loops with circular references', async () => {
438+
expect(
439+
await compileCss(css`
440+
@theme {
441+
--font-sans: 'Inter', --theme(--font-sans);
442+
}
443+
.font {
444+
font-family: var(--font-sans);
445+
}
446+
`),
447+
).toMatchInlineSnapshot(`
448+
":root, :host {
449+
--font-sans: "Inter", var(--font-sans);
450+
}
451+
452+
.font {
453+
font-family: var(--font-sans);
454+
}"
455+
`)
456+
})
436457
})
437458

438459
describe('theme(…)', () => {
@@ -903,6 +924,19 @@ describe('theme(…)', () => {
903924
}"
904925
`)
905926
})
927+
928+
test('theme(…) prevents infinite loops with circular references', async () => {
929+
await expect(
930+
compileCss(css`
931+
@theme {
932+
--font-sans: 'Inter', theme(fontFamily.sans);
933+
}
934+
.font {
935+
font-family: var(--font-sans);
936+
}
937+
`),
938+
).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Could not resolve value for theme function: \`theme(fontFamily.sans)\`. The resolved value \`'Inter', theme(fontFamily.sans)\` contains a recursive reference to itself.]`)
939+
})
906940
})
907941

908942
describe('with CSS variable syntax', () => {

packages/tailwindcss/src/css-functions.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ function legacyTheme(
145145
)
146146
}
147147

148+
// Detect eventual recursive theme function calls.
149+
let regex = new RegExp('theme\\(\\s*[\'\"]?' + path)
150+
if (typeof resolvedValue === 'string' && resolvedValue.match(regex)) {
151+
throw new Error(
152+
`Could not resolve value for theme function: \`theme(${path})\`. The resolved value \`${resolvedValue}\` contains a recursive reference to itself.`,
153+
)
154+
}
155+
148156
return resolvedValue
149157
}
150158

0 commit comments

Comments
 (0)