diff --git a/crates/rustmotion-html/src/element.rs b/crates/rustmotion-html/src/element.rs index c308265a..b370f658 100644 --- a/crates/rustmotion-html/src/element.rs +++ b/crates/rustmotion-html/src/element.rs @@ -2,7 +2,9 @@ use markup5ever_rcdom::{Handle, NodeData}; use serde_json::{Map, Value}; use crate::style::{coerce_value, parse_anim_attr, parse_inline_style}; -use crate::{element_attrs, tag_name, HtmlError}; +use crate::{check_known_attrs, element_attrs, tag_name, HtmlError}; + +const KNOWN_NATIVE_ATTRS: &[&str] = &["style", "anim"]; enum TagKind { Container, @@ -90,6 +92,7 @@ pub(crate) fn element_to_value(handle: &Handle) -> Result, HtmlErr suggestion: suggestion.to_string(), }), TagKind::Text => { + check_known_attrs(&tag, &attrs, KNOWN_NATIVE_ATTRS)?; let mut obj = Map::new(); obj.insert("type".into(), Value::from("text")); obj.insert("content".into(), Value::from(inner_text(handle))); @@ -99,6 +102,7 @@ pub(crate) fn element_to_value(handle: &Handle) -> Result, HtmlErr Ok(Some(Value::Object(obj))) } TagKind::Container => { + check_known_attrs(&tag, &attrs, KNOWN_NATIVE_ATTRS)?; let mut obj = Map::new(); obj.insert("type".into(), Value::from("div")); if let Some(style) = style_object(&attrs)? { diff --git a/crates/rustmotion-html/src/lib.rs b/crates/rustmotion-html/src/lib.rs index 092bd1d8..69d9ea43 100644 --- a/crates/rustmotion-html/src/lib.rs +++ b/crates/rustmotion-html/src/lib.rs @@ -97,6 +97,44 @@ pub enum HtmlError { " found nested inside <{parent}> — elements must be direct children of (only is recursed into)" )] NestedScene { parent: String }, + /// Emitted when an element carries an attribute the transpiler never + /// reads. ``, ``, and native container/text tags only + /// ever consume a fixed, small set of attribute names — anything else + /// used to vanish with no trace, invisible to `--strict-attrs` because + /// it never reached the emitted JSON in the first place. + #[error("<{element}> has unsupported attribute(s): {detail} — these are silently ignored today; fix the typo, drop them, or use the attribute the dialect actually reads")] + UnknownAttributes { element: String, detail: String }, + /// Emitted for a ``'s `world-position` attribute that is neither + /// `"x,y"` nor a JSON `{"x":..,"y":..}` object. + #[error("world-position=\"{0}\" is not \"x,y\" or a JSON object {{\"x\":..,\"y\":..}}")] + InvalidWorldPosition(String), + /// Emitted for a ``'s `animated-background` attribute when its + /// value starts with `{`/`[` but fails to parse as JSON. + #[error("animated-background attribute contains invalid JSON: {0}")] + InvalidAnimatedBackgroundJson(String), + /// Emitted for a `style="..."` declaration whose value has more than one + /// top-level (paren-aware) token and isn't one of the shorthands the + /// transpiler knows how to expand (`padding`/`margin`/`border-radius`'s + /// 1-4 value box form, `grid-template-columns`/`-rows`'s track list with + /// `repeat()`/`minmax()`). Every other multi-token value used to become + /// an opaque string the core length parser cannot read, silently + /// resolving to `0px`. + #[error("style property '{prop}' has an unsupported multi-token value '{value}' — supported multi-token forms are the padding/margin/border-radius box shorthand and grid-template-columns/-rows track lists with repeat()/minmax(); rewrite as a single value")] + UnsupportedStyleShorthand { prop: String, value: String }, + /// Emitted when a `