Skip to content

Commit 4d01e4a

Browse files
authored
Merge branch 'next' into feat/check-tw-version-before-upgrade
2 parents ff1b1b6 + dd3441b commit 4d01e4a

File tree

16 files changed

+576
-79
lines changed

16 files changed

+576
-79
lines changed

CHANGELOG.md

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

1212
- Reintroduce `max-w-screen-*` utilities that read from the `--breakpoint` namespace as deprecated utilities ([#15013](https://github.com/tailwindlabs/tailwindcss/pull/15013))
1313
- Allow `addUtilities()` and `addComponents()` to work with child combinators and other complex selectors ([#15029](https://github.com/tailwindlabs/tailwindcss/pull/15029))
14+
- Support using CSS variables as arbitrary values without `var(…)` by using parentheses instead of square brackets (e.g. `bg-(--my-color)`) ([#15020](https://github.com/tailwindlabs/tailwindcss/pull/15020))
15+
- Add new `in-*` variant ([#15025](https://github.com/tailwindlabs/tailwindcss/pull/15025))
16+
- _Upgrade (experimental)_: Migrate `[&>*]` to the `*` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022))
17+
- _Upgrade (experimental)_: Migrate `[&_*]` to the `**` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022))
18+
- _Upgrade (experimental)_: Warn when trying to migrating a project that is not on Tailwind CSS v3 ([#15015](https://github.com/tailwindlabs/tailwindcss/pull/15015))
1419

1520
### Fixed
1621

@@ -20,7 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2025
### Changed
2126

2227
- Bring back support for color opacity modifiers to read from `--opacity-*` theme values ([#14278](https://github.com/tailwindlabs/tailwindcss/pull/14278))
23-
- _Upgrade (experimental)_: Warn when trying to migrating a project that is not on Tailwind CSS v3 ([#15015](https://github.com/tailwindlabs/tailwindcss/pull/15015))
2428

2529
## [4.0.0-alpha.34] - 2024-11-14
2630

crates/oxide/src/parser.rs

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ pub struct ExtractorOptions {
3939
pub preserve_spaces_in_arbitrary: bool,
4040
}
4141

42+
#[derive(Debug, PartialEq, Eq, Clone)]
43+
enum Arbitrary {
44+
/// Not inside any arbitrary value
45+
None,
46+
47+
/// In arbitrary value mode with square brackets
48+
///
49+
/// E.g.: `bg-[…]`
50+
/// ^
51+
Brackets { start_idx: usize },
52+
53+
/// In arbitrary value mode with parens
54+
///
55+
/// E.g.: `bg-(…)`
56+
/// ^
57+
Parens { start_idx: usize },
58+
}
59+
4260
pub struct Extractor<'a> {
4361
opts: ExtractorOptions,
4462

@@ -48,9 +66,9 @@ pub struct Extractor<'a> {
4866
idx_start: usize,
4967
idx_end: usize,
5068
idx_last: usize,
51-
idx_arbitrary_start: usize,
5269

53-
in_arbitrary: bool,
70+
arbitrary: Arbitrary,
71+
5472
in_candidate: bool,
5573
in_escape: bool,
5674

@@ -105,9 +123,8 @@ impl<'a> Extractor<'a> {
105123

106124
idx_start: 0,
107125
idx_end: 0,
108-
idx_arbitrary_start: 0,
109126

110-
in_arbitrary: false,
127+
arbitrary: Arbitrary::None,
111128
in_candidate: false,
112129
in_escape: false,
113130

@@ -461,7 +478,7 @@ impl<'a> Extractor<'a> {
461478

462479
#[inline(always)]
463480
fn parse_arbitrary(&mut self) -> ParseAction<'a> {
464-
// In this we could technically use memchr 6 times (then looped) to find the indexes / bounds of arbitrary valuesq
481+
// In this we could technically use memchr 6 times (then looped) to find the indexes / bounds of arbitrary values
465482
if self.in_escape {
466483
return self.parse_escaped();
467484
}
@@ -479,9 +496,29 @@ impl<'a> Extractor<'a> {
479496
self.bracket_stack.pop();
480497
}
481498

482-
// Last bracket is different compared to what we expect, therefore we are not in a
483-
// valid arbitrary value.
484-
_ if !self.in_quotes() => return ParseAction::Skip,
499+
// This is the last bracket meaning the end of arbitrary content
500+
_ if !self.in_quotes() => {
501+
if matches!(self.cursor.next, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') {
502+
return ParseAction::Consume;
503+
}
504+
505+
if let Arbitrary::Parens { start_idx } = self.arbitrary {
506+
trace!("Arbitrary::End\t");
507+
self.arbitrary = Arbitrary::None;
508+
509+
if self.cursor.pos - start_idx == 1 {
510+
// We have an empty arbitrary value, which is not allowed
511+
return ParseAction::Skip;
512+
}
513+
514+
// We have a valid arbitrary value
515+
return ParseAction::Consume;
516+
}
517+
518+
// Last parenthesis is different compared to what we expect, therefore we are
519+
// not in a valid arbitrary value.
520+
return ParseAction::Skip;
521+
}
485522

486523
// We're probably in quotes or nested brackets, so we keep going
487524
_ => {}
@@ -501,12 +538,14 @@ impl<'a> Extractor<'a> {
501538
return ParseAction::Consume;
502539
}
503540

504-
trace!("Arbitrary::End\t");
505-
self.in_arbitrary = false;
541+
if let Arbitrary::Brackets { start_idx } = self.arbitrary {
542+
trace!("Arbitrary::End\t");
543+
self.arbitrary = Arbitrary::None;
506544

507-
if self.cursor.pos - self.idx_arbitrary_start == 1 {
508-
// We have an empty arbitrary value, which is not allowed
509-
return ParseAction::Skip;
545+
if self.cursor.pos - start_idx == 1 {
546+
// We have an empty arbitrary value, which is not allowed
547+
return ParseAction::Skip;
548+
}
510549
}
511550
}
512551

@@ -531,9 +570,13 @@ impl<'a> Extractor<'a> {
531570
b' ' if !self.opts.preserve_spaces_in_arbitrary => {
532571
trace!("Arbitrary::SkipAndEndEarly\t");
533572

534-
// Restart the parser ahead of the arbitrary value
535-
// It may pick up more candidates
536-
return ParseAction::RestartAt(self.idx_arbitrary_start + 1);
573+
if let Arbitrary::Brackets { start_idx } | Arbitrary::Parens { start_idx } =
574+
self.arbitrary
575+
{
576+
// Restart the parser ahead of the arbitrary value It may pick up more
577+
// candidates
578+
return ParseAction::RestartAt(start_idx + 1);
579+
}
537580
}
538581

539582
// Arbitrary values allow any character inside them
@@ -550,11 +593,12 @@ impl<'a> Extractor<'a> {
550593
#[inline(always)]
551594
fn parse_start(&mut self) -> ParseAction<'a> {
552595
match self.cursor.curr {
553-
// Enter arbitrary value mode
596+
// Enter arbitrary property mode
554597
b'[' => {
555598
trace!("Arbitrary::Start\t");
556-
self.in_arbitrary = true;
557-
self.idx_arbitrary_start = self.cursor.pos;
599+
self.arbitrary = Arbitrary::Brackets {
600+
start_idx: self.cursor.pos,
601+
};
558602

559603
ParseAction::Consume
560604
}
@@ -584,22 +628,31 @@ impl<'a> Extractor<'a> {
584628
#[inline(always)]
585629
fn parse_continue(&mut self) -> ParseAction<'a> {
586630
match self.cursor.curr {
587-
// Enter arbitrary value mode
631+
// Enter arbitrary value mode. E.g.: `bg-[rgba(0, 0, 0)]`
632+
// ^
588633
b'[' if matches!(
589634
self.cursor.prev,
590635
b'@' | b'-' | b' ' | b':' | b'/' | b'!' | b'\0'
591636
) =>
592637
{
593638
trace!("Arbitrary::Start\t");
594-
self.in_arbitrary = true;
595-
self.idx_arbitrary_start = self.cursor.pos;
639+
self.arbitrary = Arbitrary::Brackets {
640+
start_idx: self.cursor.pos,
641+
};
596642
}
597643

598-
// Can't enter arbitrary value mode
599-
// This can't be a candidate
600-
b'[' => {
601-
trace!("Arbitrary::Skip_Start\t");
644+
// Enter arbitrary value mode. E.g.: `bg-(--my-color)`
645+
// ^
646+
b'(' if matches!(self.cursor.prev, b'-' | b'/') => {
647+
trace!("Arbitrary::Start\t");
648+
self.arbitrary = Arbitrary::Parens {
649+
start_idx: self.cursor.pos,
650+
};
651+
}
602652

653+
// Can't enter arbitrary value mode. This can't be a candidate.
654+
b'[' | b'(' => {
655+
trace!("Arbitrary::Skip_Start\t");
603656
return ParseAction::Skip;
604657
}
605658

@@ -684,7 +737,7 @@ impl<'a> Extractor<'a> {
684737
#[inline(always)]
685738
fn can_be_candidate(&mut self) -> bool {
686739
self.in_candidate
687-
&& !self.in_arbitrary
740+
&& matches!(self.arbitrary, Arbitrary::None)
688741
&& (0..=127).contains(&self.cursor.curr)
689742
&& (self.idx_start == 0 || self.input[self.idx_start - 1] <= 127)
690743
}
@@ -696,13 +749,13 @@ impl<'a> Extractor<'a> {
696749
self.idx_start = self.cursor.pos;
697750
self.idx_end = self.cursor.pos;
698751
self.in_candidate = false;
699-
self.in_arbitrary = false;
752+
self.arbitrary = Arbitrary::None;
700753
self.in_escape = false;
701754
}
702755

703756
#[inline(always)]
704757
fn parse_char(&mut self) -> ParseAction<'a> {
705-
if self.in_arbitrary {
758+
if !matches!(self.arbitrary, Arbitrary::None) {
706759
self.parse_arbitrary()
707760
} else if self.in_candidate {
708761
self.parse_continue()
@@ -732,9 +785,8 @@ impl<'a> Extractor<'a> {
732785

733786
self.idx_start = pos;
734787
self.idx_end = pos;
735-
self.idx_arbitrary_start = 0;
736788

737-
self.in_arbitrary = false;
789+
self.arbitrary = Arbitrary::None;
738790
self.in_candidate = false;
739791
self.in_escape = false;
740792

@@ -977,6 +1029,18 @@ mod test {
9771029
assert_eq!(candidates, vec!["m-[2px]"]);
9781030
}
9791031

1032+
#[test]
1033+
fn it_can_parse_utilities_with_arbitrary_var_shorthand() {
1034+
let candidates = run("m-(--my-var)", false);
1035+
assert_eq!(candidates, vec!["m-(--my-var)"]);
1036+
}
1037+
1038+
#[test]
1039+
fn it_can_parse_utilities_with_arbitrary_var_shorthand_as_modifier() {
1040+
let candidates = run("bg-(--my-color)/(--my-opacity)", false);
1041+
assert_eq!(candidates, vec!["bg-(--my-color)/(--my-opacity)"]);
1042+
}
1043+
9801044
#[test]
9811045
fn it_throws_away_arbitrary_values_that_are_unbalanced() {
9821046
let candidates = run("m-[calc(100px*2]", false);

integrations/upgrade/index.test.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ test(
100100
--- ./src/index.html ---
101101
<h1>🤠👋</h1>
102102
<div
103-
class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)] max-w-[var(--breakpoint-md)] ml-[var(--breakpoint-md)]"
103+
class="flex! sm:block! bg-linear-to-t bg-(--my-red) max-w-(--breakpoint-md) ml-(--breakpoint-md)"
104104
></div>
105105
<!-- Migrate to sm -->
106106
<div class="blur-sm shadow-sm rounded-sm inset-shadow-sm drop-shadow-sm"></div>
@@ -151,9 +151,9 @@ test(
151151
candidate`flex!`,
152152
candidate`sm:block!`,
153153
candidate`bg-linear-to-t`,
154-
candidate`bg-[var(--my-red)]`,
155-
candidate`max-w-[var(--breakpoint-md)]`,
156-
candidate`ml-[var(--breakpoint-md)`,
154+
candidate`bg-(--my-red)`,
155+
candidate`max-w-(--breakpoint-md)`,
156+
candidate`ml-(--breakpoint-md)`,
157157
])
158158
},
159159
)
@@ -639,7 +639,7 @@ test(
639639
'src/index.html',
640640
// prettier-ignore
641641
js`
642-
<div class="bg-[var(--my-red)]"></div>
642+
<div class="bg-(--my-red)"></div>
643643
`,
644644
)
645645

@@ -798,7 +798,7 @@ test(
798798
'src/index.html',
799799
// prettier-ignore
800800
js`
801-
<div class="bg-[var(--my-red)]"></div>
801+
<div class="bg-(--my-red)"></div>
802802
`,
803803
)
804804

@@ -873,7 +873,7 @@ test(
873873
'src/index.html',
874874
// prettier-ignore
875875
js`
876-
<div class="bg-[var(--my-red)]"></div>
876+
<div class="bg-(--my-red)"></div>
877877
`,
878878
)
879879

@@ -1447,7 +1447,7 @@ test(
14471447
"
14481448
--- ./src/index.html ---
14491449
<div
1450-
class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)]"
1450+
class="flex! sm:block! bg-linear-to-t bg-(--my-red)"
14511451
></div>
14521452
14531453
--- ./src/root.1.css ---
@@ -1664,7 +1664,7 @@ test(
16641664
"
16651665
--- ./src/index.html ---
16661666
<div
1667-
class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)]"
1667+
class="flex! sm:block! bg-linear-to-t bg-(--my-red)"
16681668
></div>
16691669
16701670
--- ./src/index.css ---
@@ -1799,7 +1799,7 @@ test(
17991799
"
18001800
--- ./src/index.html ---
18011801
<div
1802-
class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)]"
1802+
class="flex! sm:block! bg-linear-to-t bg-(--my-red)"
18031803
></div>
18041804
18051805
--- ./src/index.css ---

packages/@tailwindcss-upgrade/src/template/candidates.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ const candidates = [
123123
['bg-[no-repeat_url(/image_13.png)]', 'bg-[no-repeat_url(/image_13.png)]'],
124124
[
125125
'bg-[var(--spacing-0_5,_var(--spacing-1_5,_3rem))]',
126-
'bg-[var(--spacing-0_5,var(--spacing-1_5,3rem))]',
126+
'bg-(--spacing-0_5,var(--spacing-1_5,3rem))',
127127
],
128128
]
129129

0 commit comments

Comments
 (0)