Parse candidates in .svelte files with class:abc="condition" syntax (#13274)

* handle `.svelte` file

In svelte there is a convention where you can use `<div
class:px-4="condition" />` which means that we extract `class:px-4` as a
utility where `class` is a variant.

This is obviously incorrect, to solve this we can ignore the `class:`
part before parsing the whole file.

This is also what we do in v3.

Ideally we have completely separate parsers for various programming
languages (based on file type) and fallback to the generic one we have
now.

Implementing that, is a much bigger scope.

* flatten match arms

* add test to verify we can parse `class:px-4="condition"`

* explicitly ingore everything but the svelte file

* merge tests

There is some funky stuff happening when running `cargo test` where it
sees contents from another test. Maybe a bug in the tmpdir crate.

I don't see this problem locally when running `cargo nextest run`, but
when using the native `cargo test` command it fails.

* update changelog

* run prettier

* Update oxide/crates/core/src/lib.rs

Co-authored-by: Jordan Pittman <jordan@cryptica.me>

* fixup syntax errors

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
This commit is contained in:
Robin Malfait 2024-03-18 23:24:22 +01:00 • committed by GitHub
parent cdb0997d6e
commit f3e78807af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 17 deletions

View file

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Validate bare values ([#13245](https://github.com/tailwindlabs/tailwindcss/pull/13245))
- Parse candidates in `.svelte` files with `class:abc="condition"` syntax ([#13274](https://github.com/tailwindlabs/tailwindcss/pull/13274))
### Changed

View file

@ -1,4 +1,5 @@
use crate::parser::Extractor;
use bstr::ByteSlice;
use cache::Cache;
use fxhash::FxHashSet;
use ignore::DirEntry;
@ -399,6 +400,32 @@ pub fn scan_files(input: Vec<ChangedContent>, options: u8) -> Vec<String> {
}
}
fn read_changed_content(c: ChangedContent) -> Option<Vec<u8>> {
if let Some(content) = c.content {
return Some(content.into_bytes());
}
let Some(file) = c.file else {
return Default::default();
};
let Ok(content) = std::fs::read(&file).map_err(|e| {
event!(tracing::Level::ERROR, "Failed to read file: {:?}", e);
e
}) else {
return Default::default();
};
let Some(extension) = file.extension().map(|x| x.to_str()) else {
return Some(content);
};
match extension {
Some("svelte") => Some(content.replace(" class:", " ")),
_ => Some(content),
}
}
#[tracing::instrument(skip(changed_content))]
fn read_all_files(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
event!(
@ -409,17 +436,7 @@ fn read_all_files(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
changed_content
.into_par_iter()
.map(|c| match (c.file, c.content) {
(Some(file), None) => match std::fs::read(file) {
Ok(content) => content,
Err(e) => {
event!(tracing::Level::ERROR, "Failed to read file: {:?}", e);
Default::default()
}
},
(None, Some(content)) => content.into_bytes(),
_ => Default::default(),
})
.filter_map(read_changed_content)
.collect()
}
@ -433,11 +450,7 @@ fn read_all_files_sync(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
changed_content
.into_iter()
.filter_map(|c| match (c.file, c.content) {
(Some(file), None) => std::fs::read(file).ok(),
(None, Some(content)) => Some(content.into_bytes()),
_ => Default::default(),
})
.filter_map(read_changed_content)
.collect()
}

View file

@ -297,9 +297,14 @@ mod auto_content {
("foo.jpg", Some("xl:font-bold")),
// A file that is ignored
("foo.html", Some("lg:font-bold")),
// A svelte file with `class:foo="bar"` syntax
("index.svelte", Some("<div class:px-4='condition'></div>")),
])
.1;
assert_eq!(candidates, vec!["font-bold", "md:flex"]);
assert_eq!(
candidates,
vec!["condition", "div", "font-bold", "md:flex", "px-4"]
);
}
}