Pre process <template lang="…"> in Vue files (#17252)

This PR fixes an issue where `<template lang="…">…</template>` in Vue
files should be handled as-if it's the language specified in the `lang`
attribute.

To do this, we added a new Vue pre processor and run the content through
the same pre processor logic as we do for other languages.

Fixes: #17211

# Test plan

1. Added a test to verify this works
2. Existing tests still work

Visually verified against the reproduction in the issue:

| Before | After |
| --- | --- |
| <img width="1273" alt="image"
src="https://github.com/user-attachments/assets/d1accdeb-97cf-48ef-83fb-978832b3e599"
/> | <img width="1273" alt="image"
src="https://github.com/user-attachments/assets/ab7ec19c-b6c4-43be-8845-096ff4e58808"
/> |

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This commit is contained in:
Robin Malfait 2025-03-17 14:54:37 +01:00 committed by GitHub
parent ebfde316e1
commit d7c81164da
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 56 additions and 9 deletions

View File

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix incorrect angle in `-bg-conic-*` utilities ([#17174](https://github.com/tailwindlabs/tailwindcss/pull/17174))
- Fix `border-[12px_4px]` being interpreted as a `border-color` instead of a `border-width` ([#17248](https://github.com/tailwindlabs/tailwindcss/pull/17248))
- Use the `oklab(…)` function when applying opacity to `currentColor` to work around a crash in Safari 16.4 and 16.5 ([#17247](https://github.com/tailwindlabs/tailwindcss/pull/17247))
- Pre-process `<template lang="…">` in Vue files ([#17252](https://github.com/tailwindlabs/tailwindcss/pull/17252))
## [4.0.14] - 2025-03-13

14
Cargo.lock generated
View File

@ -410,13 +410,14 @@ dependencies = [
[[package]]
name = "regex"
version = "1.8.3"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.7.2",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
@ -445,12 +446,6 @@ version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "regex-syntax"
version = "0.8.5"
@ -548,6 +543,7 @@ dependencies = [
"ignore",
"log",
"rayon",
"regex",
"rustc-hash",
"tempfile",
"tracing",

View File

@ -18,6 +18,7 @@ dunce = "1.0.5"
bexpand = "1.2.0"
fast-glob = "0.4.3"
classification-macros = { path = "../classification-macros" }
regex = "1.11.1"
[dev-dependencies]
tempfile = "3.13.0"

View File

@ -7,6 +7,7 @@ pub mod razor;
pub mod ruby;
pub mod slim;
pub mod svelte;
pub mod vue;
pub use clojure::*;
pub use haml::*;
@ -17,3 +18,4 @@ pub use razor::*;
pub use ruby::*;
pub use slim::*;
pub use svelte::*;
pub use vue::*;

View File

@ -0,0 +1,46 @@
use crate::extractor::pre_processors::pre_processor::PreProcessor;
use crate::pre_process_input;
use bstr::ByteSlice;
use regex::Regex;
use std::sync;
static TEMPLATE_REGEX: sync::LazyLock<Regex> = sync::LazyLock::new(|| {
Regex::new(r#"<template lang=['"]([^"']*)['"]>([\s\S]*)<\/template>"#).unwrap()
});
#[derive(Debug, Default)]
pub struct Vue;
impl PreProcessor for Vue {
fn process(&self, content: &[u8]) -> Vec<u8> {
let mut result = content.to_vec();
let content_as_str = std::str::from_utf8(content).unwrap();
for (_, [lang, body]) in TEMPLATE_REGEX
.captures_iter(content_as_str)
.map(|c| c.extract())
{
let replaced = pre_process_input(body.as_bytes(), lang);
result = result.replace(body, replaced);
}
result
}
}
#[cfg(test)]
mod tests {
use super::Vue;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_vue_template_pug() {
let input = r#"
<template lang="pug">
.bg-neutral-900.text-red-500 This is a test.
</template>
"#;
Vue::test_extract_contains(input, vec!["bg-neutral-900", "text-red-500"]);
}
}

View File

@ -476,6 +476,7 @@ pub fn pre_process_input(content: &[u8], extension: &str) -> Vec<u8> {
"rb" | "erb" => Ruby.process(content),
"slim" => Slim.process(content),
"svelte" => Svelte.process(content),
"vue" => Vue.process(content),
_ => content.to_vec(),
}
}