Add JSON pre-processor (#17125)

This PR adds a small JSON pre processor to improve parsing JSON files.

Due to the sheer amount of potential `[` and `]` brackets, it could be
that parsing JSON files are way slower than they need to be.

We saw this while debugging this issue:
https://github.com/tailwindlabs/tailwindcss/issues/17092


# Test plan

1. Added test to verify the pre processing works
2. Existing tests still pass
This commit is contained in:
Robin Malfait 2025-03-11 13:15:48 +01:00 • committed by GitHub
parent 8bc633bb17
commit 9ddeb091d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 0 deletions

View file

@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure classes between `>` and `<` are properly extracted ([#17094](https://github.com/tailwindlabs/tailwindcss/pull/17094))
- Treat starting single quote as verbatim text in Slim ([#17085](https://github.com/tailwindlabs/tailwindcss/pull/17085))
- Ensure `.node` and `.wasm` files are not scanned for utilities ([#17123](https://github.com/tailwindlabs/tailwindcss/pull/17123))
- Improve performance when scanning `JSON` files ([#17125](https://github.com/tailwindlabs/tailwindcss/pull/17125))
## [4.0.12] - 2025-03-07

View file

@ -0,0 +1,63 @@
use crate::cursor;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[derive(Debug, Default)]
pub struct Json;
impl PreProcessor for Json {
fn process(&self, content: &[u8]) -> Vec<u8> {
let len = content.len();
let mut result = content.to_vec();
let mut cursor = cursor::Cursor::new(content);
while cursor.pos < len {
match cursor.curr {
// Consume strings as-is
b'"' => {
cursor.advance();
while cursor.pos < len {
match cursor.curr {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
// End of the string
b'"' => break,
// Everything else is valid
_ => cursor.advance(),
};
}
}
// Replace brackets and curlies with spaces
b'[' | b'{' | b']' | b'}' => {
result[cursor.pos] = b' ';
}
// Consume everything else
_ => {}
};
cursor.advance();
}
result
}
}
#[cfg(test)]
mod tests {
use super::Json;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_json_pre_processor() {
let (input, expected) = (
r#"[1,[2,[3,4,["flex flex-1 content-['hello_world']"]]], {"flex": true}]"#,
r#" 1, 2, 3,4, "flex flex-1 content-['hello_world']" , "flex": true "#,
);
Json::test(input, expected);
}
}

View file

@ -1,4 +1,5 @@
pub mod haml;
pub mod json;
pub mod pre_processor;
pub mod pug;
pub mod razor;
@ -7,6 +8,7 @@ pub mod slim;
pub mod svelte;
pub use haml::*;
pub use json::*;
pub use pre_processor::*;
pub use pug::*;
pub use razor::*;

View file

@ -470,6 +470,7 @@ pub fn pre_process_input(content: &[u8], extension: &str) -> Vec<u8> {
match extension {
"cshtml" | "razor" => Razor.process(content),
"haml" => Haml.process(content),
"json" => Json.process(content),
"pug" => Pug.process(content),
"rb" | "erb" => Ruby.process(content),
"slim" => Slim.process(content),