Fix stripe-child edge and token matching

AI-assisted changes requested by pbakaus. Preserve winning auto inset longhands and require complete Tailwind cue tokens. Added regressions that fail before the fixes; focused tests, cargo test --workspace, release build, and the full rebuilt-engine Bun/Node suite pass.
This commit is contained in:
Paul Bakaus
2026-09-14 18:36:05 -07:00
parent afc0596903
commit 8b0cc87023
3 changed files with 31 additions and 22 deletions
+16 -1
View File
@@ -942,7 +942,10 @@ pub static REGEX_MATCHERS: Lazy<Vec<Matcher>> = Lazy::new(|| {
let scope = containing_markup_tag(line)(m.index);
find_solid_chromatic_bg(&scope).is_some()
&& stripe_child_markup_empty(line, m.index)
&& STRIPE_CHILD_CUE_RE.is_match(&scope)
&& STRIPE_CHILD_CUE_RE.find_iter(&scope).any(|cue| {
hyphen_safe_prefix(&scope, cue.start())
&& hyphen_safe_suffix(&scope, cue.end())
})
&& !scope_has_fixed_height(&scope)
&& !STRIPE_CHILD_ROUNDED_FULL_RE.is_match(&scope)
&& !STRIPE_CHILD_ARIA_RE.is_match(&scope)
@@ -1522,6 +1525,18 @@ mod tests {
);
}
#[test]
fn stripe_child_cues_require_complete_class_tokens() {
for cue in ["left-0.5", "right-0.5", "inset-y-0.5", "-left-0", "left-0/2", "shrink-0.5"] {
let source = format!(r#"<div className="w-1 {cue} bg-amber-500" />"#);
assert!(run("side-tab", &source).is_empty(), "{cue}");
}
for cue in ["left-0", "right-0", "inset-y-0", "shrink-0", "rounded-l-lg"] {
let source = format!(r#"<div className="w-1 {cue} bg-amber-500" />"#);
assert_eq!(run("side-tab", &source).len(), 1, "{cue}");
}
}
#[test]
fn stripe_child_tailwind() {
let s = |line: &str| run("side-tab", line);
+4 -21
View File
@@ -9,7 +9,7 @@ use crate::background::{
a_ge, a_gt, read_own_background_color, resolve_background, resolve_background_info,
resolve_border_radius_px, resolve_gradient_stops, sv, sv_opt, CustomPropMap,
};
use crate::cascade::{expand_static_box_values, split_css_tokens, StyleValues};
use crate::cascade::StyleValues;
use crate::dom::{StaticDocument, StaticElement};
use crate::quality::{collapse_ws, pf0, resolve_font_size_px};
use impeccable_core::checks::css_scan::css_length_to_px;
@@ -469,25 +469,6 @@ fn static_edge_hugs(value: &str) -> bool {
n.is_finite() && n.abs() <= 2.0
}
fn static_resolved_inset(style: &StyleValues) -> [String; 4] {
let mut out = [
sv(style, "top").to_string(),
sv(style, "right").to_string(),
sv(style, "bottom").to_string(),
sv(style, "left").to_string(),
];
let inset = sv(style, "inset");
if !inset.is_empty() {
let expanded = expand_static_box_values(&split_css_tokens(inset));
for (i, val) in expanded.into_iter().enumerate() {
if out[i].is_empty() || out[i] == "auto" {
out[i] = val;
}
}
}
out
}
/// JS: checks.mjs#checkElementStripeChild(el, style)
pub fn check_element_stripe_child(el: &StaticElement<'_>, style: &StyleValues) -> Vec<RuleHit> {
let tag = el.tag_lower();
@@ -518,7 +499,9 @@ pub fn check_element_stripe_child(el: &StaticElement<'_>, style: &StyleValues) -
let host_style = host.style();
let edge = if position == "absolute" || position == "fixed" {
let height_raw = sv(style, "height");
let inset = static_resolved_inset(style);
// The cascade already expands inset; a winning `auto` longhand
// must not be overwritten by the earlier shorthand.
let inset = ["top", "right", "bottom", "left"].map(|prop| sv(style, prop));
let height_stretches =
height_raw == "100%" || (static_edge_hugs(&inset[0]) && static_edge_hugs(&inset[2]));
if !height_stretches {
+11
View File
@@ -13,6 +13,17 @@ fn side_tab_snippets(html: &str) -> Vec<String> {
.collect()
}
#[test]
fn winning_auto_longhand_is_not_replaced_by_inset() {
let html = r#"<html><body><div style="position:relative;width:320px;height:100px">
<div class="stripe" style="position:absolute;inset:0;left:auto;width:4px;background:#3b82f6"></div>
</div></body></html>"#;
let hits = side_tab_snippets(html);
assert_eq!(hits.len(), 1);
assert!(hits[0].contains("stripe child (right)"), "{hits:?}");
assert!(side_tab_snippets(&html.replace("left:auto", "left:auto;right:auto")).is_empty());
}
#[test]
fn flex_row_first_child_flags() {
let html = r#"<!DOCTYPE html><html><head><style>