From 425ee8e32cb07ef6d24e8ae8a19ad11efd4ad20a Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sat, 7 Nov 2026 05:23:00 +0100 Subject: [PATCH] Fix Tailwind export breaking JSX when font family has quotes or brackets Found while testing crates/engine/src/dev/tailwind.rs:300 (the "font-family" arm, via arb at :103) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/dev/tailwind.rs | 6 +++++- crates/engine/src/tailwind_tests.rs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/dev/tailwind.rs b/crates/engine/src/dev/tailwind.rs index 8279ef9ce..a29c0f316 100644 --- a/crates/engine/src/dev/tailwind.rs +++ b/crates/engine/src/dev/tailwind.rs @@ -297,7 +297,11 @@ fn decl(key: &str, value: &str) -> Vec<(String, String)> { ("transform-origin", "top left" | "0 0") => kw("origin-top-left"), // `font-(--x)` would read as a weight; the hint makes an un-namespaced variable a family. ("font-family", v) if var_name(v).is_some() => one(util("font", "font", v, "family-name:")), - ("font-family", v) => one(format!("font-{}", arb(&format!("'{}'", v.trim_matches('"'))))), + ("font-family", v) => { + // Quotes, backslashes and brackets would end the JSX attribute or the arbitrary value. + let family: String = v.chars().filter(|c| !matches!(c, '"' | '\\' | '[' | ']' | '\'')).collect(); + one(format!("font-{}", arb(&format!("'{family}'")))) + } ("font-size", v) => one(util("text", "text", v, "length:")), ("font-weight", v) => one(font_weight(v)), ("font-style", "italic") => kw("italic"), diff --git a/crates/engine/src/tailwind_tests.rs b/crates/engine/src/tailwind_tests.rs index 9e41feccc..163e3580c 100644 --- a/crates/engine/src/tailwind_tests.rs +++ b/crates/engine/src/tailwind_tests.rs @@ -340,3 +340,16 @@ fn per_corner_radii_with_a_variable_first_keep_every_corner() { classes("border-radius: var(--radius-a, 4px) 8px 8px 8px;\nborder-radius: var(--radius-card, 4px);\n").into_iter().map(|(_, c)| c).collect(); assert_eq!(got, ["rounded-[var(--radius-a,_4px)_8px_8px_8px]", "rounded-card"]); } + +#[test] +fn font_family_with_quote_and_bracket_stays_a_valid_class_token() { + let mut s = Session::new(); + let t = idv(&ex(&mut s, "text.create", json!({"text": "hi", "x": 0, "y": 0, "props": {"fontFamily": "A\"B]C"}}))); + let out = code(&mut s, t, json!({})); + let rest = out.split("className=\"").nth(1).unwrap_or_default(); + let (attr, after) = rest.split_once('"').unwrap_or_default(); + let family = attr.split(' ').find(|c| c.starts_with("font-[")).unwrap_or_default(); + let inner = family.strip_prefix("font-[").and_then(|f| f.strip_suffix(']')).unwrap_or("\\"); + assert!(after.starts_with('>') && !attr.contains('\\'), "{out}"); + assert!(!inner.is_empty() && !inner.contains(['\\', '"', ']']), "{out}"); +}