mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Fix scanning classes delimited by tab characters (#15169)
This PR fixes an issue where multi-line candidates in Svelte files couldn't be found as reported in #15148 After digging in, the real culprit seems to be that the reproduction used tab `\t` characters instead of spaces and we only delimited explicitly on spaces. Initially I couldn't reproduce this in an integration test until we (@thecrypticace and I) realised that `\t` was being used. ## Test plan: This PR adds an integration test that fails before the fix happens. The fix itself is easy in the sense that we just use all ascii whitespace characters instead of just spaces. Fixes: #15148
This commit is contained in:
parent
8ae8f65f86
commit
4bdc724a22
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Ensure `.group` and `.peer` are prefixed when using the `prefix(…)` option ([#15174](https://github.com/tailwindlabs/tailwindcss/pull/15174))
|
||||
- Ensure 3D transforms render correctly in Safari ([#15179](https://github.com/tailwindlabs/tailwindcss/pull/15179))
|
||||
- Ensure `--spacing-*` variables take precedence over `--container-*` variables ([#15180](https://github.com/tailwindlabs/tailwindcss/pull/15180))
|
||||
- Fix scanning classes delimited by tab characters ([#15169](https://github.com/tailwindlabs/tailwindcss/pull/15169))
|
||||
|
||||
## [4.0.0-beta.2] - 2024-11-22
|
||||
|
||||
|
||||
@ -253,9 +253,7 @@ impl<'a> Extractor<'a> {
|
||||
|
||||
// Reject candidates that are single camelCase words, e.g.: `useEffect`
|
||||
if candidate.iter().all(|c| c.is_ascii_alphanumeric())
|
||||
&& candidate
|
||||
.iter()
|
||||
.any(|c| c.is_ascii_uppercase())
|
||||
&& candidate.iter().any(|c| c.is_ascii_uppercase())
|
||||
{
|
||||
return ValidationResult::Invalid;
|
||||
}
|
||||
@ -570,7 +568,7 @@ impl<'a> Extractor<'a> {
|
||||
}
|
||||
},
|
||||
|
||||
b' ' if !self.opts.preserve_spaces_in_arbitrary => {
|
||||
c if c.is_ascii_whitespace() && !self.opts.preserve_spaces_in_arbitrary => {
|
||||
trace!("Arbitrary::SkipAndEndEarly\t");
|
||||
|
||||
if let Arbitrary::Brackets { start_idx } | Arbitrary::Parens { start_idx } =
|
||||
@ -633,10 +631,8 @@ impl<'a> Extractor<'a> {
|
||||
match self.cursor.curr {
|
||||
// Enter arbitrary value mode. E.g.: `bg-[rgba(0, 0, 0)]`
|
||||
// ^
|
||||
b'[' if matches!(
|
||||
self.cursor.prev,
|
||||
b'@' | b'-' | b' ' | b':' | b'/' | b'!' | b'\0'
|
||||
) =>
|
||||
b'[' if matches!(self.cursor.prev, b'@' | b'-' | b':' | b'/' | b'!' | b'\0')
|
||||
|| self.cursor.prev.is_ascii_whitespace() =>
|
||||
{
|
||||
trace!("Arbitrary::Start\t");
|
||||
self.arbitrary = Arbitrary::Brackets {
|
||||
@ -668,7 +664,8 @@ impl<'a> Extractor<'a> {
|
||||
(true, _) => ParseAction::Consume,
|
||||
|
||||
// Looks like the end of a candidate == okay
|
||||
(_, b' ' | b'\'' | b'"' | b'`') => ParseAction::Consume,
|
||||
(_, b'\'' | b'"' | b'`') => ParseAction::Consume,
|
||||
(_, c) if c.is_ascii_whitespace() => ParseAction::Consume,
|
||||
|
||||
// Otherwise, not a valid character in a candidate
|
||||
_ => ParseAction::Skip,
|
||||
@ -1542,17 +1539,98 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn simple_utility_names_with_numbers_work() {
|
||||
let candidates = run(r#"<div class="h2 hz"></div>"#, false);
|
||||
assert_eq!(candidates, vec!["div", "class", "h2", "hz",]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classes_in_an_array_without_whitespace() {
|
||||
let candidates = run(
|
||||
r#"<div class="h2 hz"></div>"#,
|
||||
"let classes = ['bg-black','hover:px-0.5','text-[13px]','[--my-var:1_/_2]','[.foo_&]:px-[0]','[.foo_&]:[color:red]']",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
candidates,
|
||||
vec![
|
||||
"div",
|
||||
"class",
|
||||
"h2",
|
||||
"hz",
|
||||
"let",
|
||||
"classes",
|
||||
"bg-black",
|
||||
"hover:px-0.5",
|
||||
"text-[13px]",
|
||||
"[--my-var:1_/_2]",
|
||||
"--my-var:1_/_2",
|
||||
"[.foo_&]:px-[0]",
|
||||
"[.foo_&]:[color:red]",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classes_in_an_array_with_spaces() {
|
||||
let candidates = run(
|
||||
"let classes = ['bg-black', 'hover:px-0.5', 'text-[13px]', '[--my-var:1_/_2]', '[.foo_&]:px-[0]', '[.foo_&]:[color:red]']",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
candidates,
|
||||
vec![
|
||||
"let",
|
||||
"classes",
|
||||
"bg-black",
|
||||
"hover:px-0.5",
|
||||
"text-[13px]",
|
||||
"[--my-var:1_/_2]",
|
||||
"--my-var:1_/_2",
|
||||
"[.foo_&]:px-[0]",
|
||||
"[.foo_&]:[color:red]",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classes_in_an_array_with_tabs() {
|
||||
let candidates = run(
|
||||
"let classes = ['bg-black',\t'hover:px-0.5',\t'text-[13px]',\t'[--my-var:1_/_2]',\t'[.foo_&]:px-[0]',\t'[.foo_&]:[color:red]']",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
candidates,
|
||||
vec![
|
||||
"let",
|
||||
"classes",
|
||||
"bg-black",
|
||||
"hover:px-0.5",
|
||||
"text-[13px]",
|
||||
"[--my-var:1_/_2]",
|
||||
"--my-var:1_/_2",
|
||||
"[.foo_&]:px-[0]",
|
||||
"[.foo_&]:[color:red]",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classes_in_an_array_with_newlines() {
|
||||
let candidates = run(
|
||||
"let classes = [\n'bg-black',\n'hover:px-0.5',\n'text-[13px]',\n'[--my-var:1_/_2]',\n'[.foo_&]:px-[0]',\n'[.foo_&]:[color:red]'\n]",
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
candidates,
|
||||
vec![
|
||||
"let",
|
||||
"classes",
|
||||
"bg-black",
|
||||
"hover:px-0.5",
|
||||
"text-[13px]",
|
||||
"[--my-var:1_/_2]",
|
||||
"--my-var:1_/_2",
|
||||
"[.foo_&]:px-[0]",
|
||||
"[.foo_&]:[color:red]",
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user