Skip to content

Commit cb5b7b4

Browse files
Skip candidates with invalid theme() calls (tailwindlabs#14437)
Right now when we encounter a candidates with invalid `theme()` calls we throw an error which stops the build entirely. This is not ideal because, especially in the case of `node_modules`, if one file in one package has an error it will stop the build for an entire project and tracking this down can be quite difficult. Now, after this PR, any candidates that use `theme(…)` with non-existent theme keys (e.g. `rounded-[theme(--radius-does-not-exist)]`) will be skipped instead of breaking the build. Before: ```html <div class="underline rounded-[theme(--radius-does-not-exist)]"></div> ``` ```css /* No CSS was generated because an error was thrown */ /* Error: Invalid theme key: --radius-does-not-exist */ ``` After: ```html <div class="underline rounded-[theme(--radius-does-not-exist)]"></div> ``` ```css .underline { text-decoration-line: underline; } ``` --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
1 parent 23cee5c commit cb5b7b4

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Support `borderRadius.*` as an alias for `--radius-*` when using dot notation inside the `theme()` function ([#14436](https://github.com/tailwindlabs/tailwindcss/pull/14436))
1717
- Ensure individual variants from groups are always sorted earlier than stacked variants from the same groups ([#14431](https://github.com/tailwindlabs/tailwindcss/pull/14431))
1818
- Allow `anchor-size(…)` in arbitrary values ([#14394](https://github.com/tailwindlabs/tailwindcss/pull/14394))
19+
- Skip candidates with invalid `theme()` calls ([#14437](https://github.com/tailwindlabs/tailwindcss/pull/14437))
1920

2021
## [4.0.0-alpha.24] - 2024-09-11
2122

packages/tailwindcss/src/compile.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { decl, rule, walk, WalkAction, type AstNode, type Rule } from './ast'
22
import { type Candidate, type Variant } from './candidate'
3+
import { substituteFunctions } from './css-functions'
34
import { type DesignSystem } from './design-system'
45
import GLOBAL_PROPERTY_ORDER from './property-order'
56
import { asColor, type Utility } from './utilities'
@@ -40,6 +41,22 @@ export function compileCandidates(
4041
let rules = designSystem.compileAstNodes(candidate)
4142
if (rules.length === 0) continue
4243

44+
// Arbitrary values (`text-[theme(--color-red-500)]`) and arbitrary
45+
// properties (`[--my-var:theme(--color-red-500)]`) can contain function
46+
// calls so we need evaluate any functions we find there that weren't in
47+
// the source CSS.
48+
try {
49+
substituteFunctions(
50+
rules.map(({ node }) => node),
51+
designSystem.resolveThemeValue,
52+
)
53+
} catch (err) {
54+
// If substitution fails then the candidate likely contains a call to
55+
// `theme()` that is invalid which may be because of incorrect usage,
56+
// invalid arguments, or a theme key that does not exist.
57+
continue
58+
}
59+
4360
found = true
4461

4562
for (let { node, propertySort } of rules) {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,42 @@ describe('theme function', () => {
606606
}"
607607
`)
608608
})
609+
610+
test("values that don't exist don't produce candidates", async () => {
611+
// This guarantees that valid candidates still make it through when some are invalid
612+
expect(
613+
await compileCss(
614+
css`
615+
@tailwind utilities;
616+
@theme reference {
617+
--radius-sm: 2rem;
618+
}
619+
`,
620+
[
621+
'rounded-[theme(--radius-sm)]',
622+
'rounded-[theme(i.do.not.exist)]',
623+
'rounded-[theme(--i-do-not-exist)]',
624+
],
625+
),
626+
).toMatchInlineSnapshot(`
627+
".rounded-\\[theme\\(--radius-sm\\)\\] {
628+
border-radius: 2rem;
629+
}"
630+
`)
631+
632+
// This guarantees no output for the following candidates
633+
expect(
634+
await compileCss(
635+
css`
636+
@tailwind utilities;
637+
@theme reference {
638+
--radius-sm: 2rem;
639+
}
640+
`,
641+
['rounded-[theme(i.do.not.exist)]', 'rounded-[theme(--i-do-not-exist)]'],
642+
),
643+
).toEqual('')
644+
})
609645
})
610646

611647
describe('in @media queries', () => {

packages/tailwindcss/src/index.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,6 @@ export async function compile(
385385
return compiledCss
386386
}
387387

388-
// Arbitrary values (`text-[theme(--color-red-500)]`) and arbitrary
389-
// properties (`[--my-var:theme(--color-red-500)]`) can contain function
390-
// calls so we need evaluate any functions we find there that weren't in
391-
// the source CSS.
392-
substituteFunctions(newNodes, designSystem.resolveThemeValue)
393-
394388
previousAstNodeCount = newNodes.length
395389

396390
tailwindUtilitiesNode.nodes = newNodes

0 commit comments

Comments
 (0)