mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2026-01-25 16:44:12 +00:00
Fix class extraction for Rails' strict locals (#19525)
Fixes: #19481 This PR improves the Ruby extractor to better handle strict locals. We recently introduced skipping comments in the Ruby extractor (PR #19243 for #19239) by ignoring comments that start with `#` until the end of the line. Strict locals are implemented like this: ```ruby <%# locals: (css: "text-amber-600") %> ``` Notice the `#` after the `<%`, we considered this a comment and ignored it. This PR changes that behavior slightly where we skip comments that are preceded by `%`. This means that `<%# anything here _will_ be scanned %>`. This should solve the strict locals case, and normal comments will still be skipped. We can be more strict in the future if needed, but I think that this should be a good solution for both scenarios. ### Test plan 1. Added a test to ensure we extract candidates in strict locals 2. Added a regression test for issue #19239 where we introduced skipping comments in the Ruby extractor 3. Other existing tests are still passing We can also verify the extracted candidates: (it's subtle, but you can see that the class is being extracted now) <img width="1187" height="1376" alt="image" src="https://github.com/user-attachments/assets/74bbfd79-9db4-4a5b-bd8d-25f1565c6bfd" />
This commit is contained in:
parent
d5beb9534e
commit
25f7ccff80
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Allow whitespace around `@source inline()` argument ([#19461](https://github.com/tailwindlabs/tailwindcss/pull/19461))
|
||||
- CLI: Emit comment when source maps are saved to files ([#19447](https://github.com/tailwindlabs/tailwindcss/pull/19447))
|
||||
- Detect utilities when containing capital letters followed by numbers ([#19465](https://github.com/tailwindlabs/tailwindcss/pull/19465))
|
||||
- Fix class extraction for Rails' strict locals ([#19525](https://github.com/tailwindlabs/tailwindcss/pull/19525))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@ -25,7 +25,33 @@ pub trait PreProcessor: Sized + Default {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_extract_contains(input: &str, items: Vec<&str>) {
|
||||
fn test_extract_exact(input: &str, expected: Vec<&str>) {
|
||||
use crate::extractor::{Extracted, Extractor};
|
||||
|
||||
let input = input.as_bytes();
|
||||
|
||||
let processor = Self::default();
|
||||
let transformed = processor.process(input);
|
||||
|
||||
let extracted = Extractor::new(&transformed).extract();
|
||||
|
||||
// Extract all candidates and css variables.
|
||||
let candidates = extracted
|
||||
.iter()
|
||||
.filter_map(|x| match x {
|
||||
Extracted::Candidate(bytes) => std::str::from_utf8(bytes).ok(),
|
||||
Extracted::CssVariable(bytes) => std::str::from_utf8(bytes).ok(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if candidates != expected {
|
||||
dbg!(&candidates, &expected);
|
||||
panic!("Extracted candidates do not match expected candidates");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_extract_contains(input: &str, expected: Vec<&str>) {
|
||||
use crate::extractor::{Extracted, Extractor};
|
||||
|
||||
let input = input.as_bytes();
|
||||
@ -46,7 +72,7 @@ pub trait PreProcessor: Sized + Default {
|
||||
|
||||
// Ensure all items are present in the candidates.
|
||||
let mut missing = vec![];
|
||||
for item in &items {
|
||||
for item in &expected {
|
||||
if !candidates.contains(item) {
|
||||
missing.push(item);
|
||||
}
|
||||
|
||||
@ -119,7 +119,11 @@ impl PreProcessor for Ruby {
|
||||
}
|
||||
|
||||
// Replace comments in Ruby files
|
||||
b'#' => {
|
||||
//
|
||||
// Except for strict locals, these are defined in a `<%# locals: … %>`. Checking if
|
||||
// the comment is preceded by a `%` should be enough without having to perform more
|
||||
// parsing logic. Worst case we _do_ scan a few comments.
|
||||
b'#' if !matches!(cursor.prev, b'%') => {
|
||||
result[cursor.pos] = b' ';
|
||||
cursor.advance();
|
||||
|
||||
@ -382,4 +386,45 @@ mod tests {
|
||||
"#;
|
||||
Ruby::test_extract_contains(input, vec!["z-1", "z-2", "z-3"]);
|
||||
}
|
||||
|
||||
// https://github.com/tailwindlabs/tailwindcss/issues/19239
|
||||
#[test]
|
||||
fn test_skip_comments() {
|
||||
let input = r#"
|
||||
# From activerecord-8.1.1/lib/active_record/errors.rb:147
|
||||
# Rails uses RDoc cross-reference syntax in inline documentation:
|
||||
# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]
|
||||
"#;
|
||||
|
||||
// Nothing should be extracted from comments, so expect an empty array.
|
||||
Ruby::test_extract_exact(input, vec![]);
|
||||
}
|
||||
|
||||
// https://github.com/tailwindlabs/tailwindcss/issues/19481
|
||||
#[test]
|
||||
fn test_strict_locals() {
|
||||
// Strict locals are defined in a `<%# locals: … %>`, but the `#` looks like a comment
|
||||
// which we should not ignore in this case.
|
||||
let input = r#"
|
||||
<%# locals: (css: "text-amber-600") %>
|
||||
<% more_css = "text-sky-500" %>
|
||||
|
||||
<p class="text-green-500">
|
||||
In a partial
|
||||
</p>
|
||||
|
||||
<p class="<%= css %>">
|
||||
In a partial using explicit local variables
|
||||
</p>
|
||||
|
||||
<p class="<%= more_css %>">
|
||||
In a partial using explicit local variables
|
||||
</p>
|
||||
"#;
|
||||
|
||||
Ruby::test_extract_contains(
|
||||
input,
|
||||
vec!["text-amber-600", "text-sky-500", "text-green-500"],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user