Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Philipp Spiess
336a7dee0a Fix recursion in legacy CSS theme(…) function 2025-05-09 18:24:47 +02:00
3 changed files with 45 additions and 1 deletions

View file

@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- Nothing yet! ### Fixed
- Fix infinite loop when `theme()` function is called recursively inside `@theme` blocks
## [4.1.6] - 2025-05-09 ## [4.1.6] - 2025-05-09

View file

@ -433,6 +433,27 @@ describe('--theme(…)', () => {
}" }"
`) `)
}) })
test('--theme(…) prevents infinite loops with circular references', async () => {
expect(
await compileCss(css`
@theme {
--font-sans: 'Inter', --theme(--font-sans);
}
.font {
font-family: var(--font-sans);
}
`),
).toMatchInlineSnapshot(`
":root, :host {
--font-sans: "Inter", var(--font-sans);
}
.font {
font-family: var(--font-sans);
}"
`)
})
}) })
describe('theme(…)', () => { describe('theme(…)', () => {
@ -903,6 +924,19 @@ describe('theme(…)', () => {
}" }"
`) `)
}) })
test('theme(…) prevents infinite loops with circular references', async () => {
await expect(
compileCss(css`
@theme {
--font-sans: 'Inter', theme(fontFamily.sans);
}
.font {
font-family: var(--font-sans);
}
`),
).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.]`)
})
}) })
describe('with CSS variable syntax', () => { describe('with CSS variable syntax', () => {

View file

@ -145,6 +145,14 @@ function legacyTheme(
) )
} }
// Detect eventual recursive theme function calls.
let regex = new RegExp('theme\\(\\s*[\'\"]?' + path)
if (typeof resolvedValue === 'string' && resolvedValue.match(regex)) {
throw new Error(
`Could not resolve value for theme function: \`theme(${path})\`. The resolved value \`${resolvedValue}\` contains a recursive reference to itself.`,
)
}
return resolvedValue return resolvedValue
} }