Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Remove redundant `line-height: initial` from Preflight ([#15212](https://github.com/tailwindlabs/tailwindcss/pull/15212))
- Increase Standalone hardware compatibility on macOS x64 builds ([#17267](https://github.com/tailwindlabs/tailwindcss/pull/17267))
- Ensure that the CSS file rebuilds if a new CSS variable is used from templates ([#17301](https://github.com/tailwindlabs/tailwindcss/pull/17301))
- Fix class extraction followed by `(` in Pug ([#17320](https://github.com/tailwindlabs/tailwindcss/pull/17320))

### Changed

Expand Down
2 changes: 1 addition & 1 deletion crates/oxide/src/extractor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ mod tests {
// Quoted attribute
(
r#"input(type="checkbox" class="px-2.5")"#,
vec!["checkbox", "class", "px-2.5"],
vec!["input", "type", "checkbox", "class", "px-2.5"],
),
] {
assert_extract_sorted_candidates(&pre_process_input(input, "pug"), expected);
Expand Down
46 changes: 45 additions & 1 deletion crates/oxide/src/extractor/pre_processors/pug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ impl PreProcessor for Pug {
}
}

// In Pug the class name shorthand can be followed by a parenthesis. E.g.:
//
// ```pug
// body.border-t-4.p-8(attr=value)
// ^ Not part of the p-8 class
// ```
//
// This means that we need to replace all these `(` and `)` with spaces to make
// sure that we can extract the `p-8`.
//
// However, we also need to make sure that we keep the parens that are part of the
// utility class. E.g.: `bg-(--my-color)`.
b'(' if bracket_stack.is_empty() && !matches!(cursor.prev, b'-' | b'/') => {
result[cursor.pos] = b' ';
bracket_stack.push(cursor.curr);
}

b'(' | b'[' | b'{' => {
bracket_stack.push(cursor.curr);
}
Expand Down Expand Up @@ -87,7 +104,7 @@ mod tests {
("div.flex.bg-red-500", "div flex bg-red-500"),
(".flex.bg-red-500", " flex bg-red-500"),
// Keep dots in strings
(r#"div(class="px-2.5")"#, r#"div(class="px-2.5")"#),
(r#"div(class="px-2.5")"#, r#"div class="px-2.5")"#),
// Nested brackets
(
"bg-[url(https://example.com/?q=[1,2])]",
Expand Down Expand Up @@ -134,4 +151,31 @@ mod tests {
"#;
Pug::test_extract_contains(input, vec!["flex", "items-center"]);
}

// https://github.com/tailwindlabs/tailwindcss/issues/17313
#[test]
fn test_class_shorthand_followed_by_parens() {
let input = r#"
.text-sky-600.bg-neutral-900(title="A tooltip") This div has an HTML attribute.
"#;
Pug::test_extract_contains(input, vec!["text-sky-600", "bg-neutral-900"]);

// Additional test with CSS Variable shorthand syntax in the attribute itself because `(`
// and `)` are not valid in the class shorthand version.
//
// Also included an arbitrary value including `(` and `)` to make sure that we don't
// accidentally remove those either.
let input = r#"
.p-8(class="bg-(--my-color) bg-(--my-color)/(--my-opacity) bg-[url(https://example.com)]")
"#;
Pug::test_extract_contains(
input,
vec![
"p-8",
"bg-(--my-color)",
"bg-(--my-color)/(--my-opacity)",
"bg-[url(https://example.com)]",
],
);
}
}
2 changes: 1 addition & 1 deletion crates/oxide/src/extractor/pre_processors/slim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl PreProcessor for Slim {
bracket_stack.push(cursor.curr);
}

// In slim the class name shorthand can be followed by a parenthesis. E.g.:
// In Slim the class name shorthand can be followed by a parenthesis. E.g.:
//
// ```slim
// body.border-t-4.p-8(attr=value)
Expand Down