fix(build): reject unsupported slhx events
Validate static data-slhx-on values against the runtime-supported delegated event set so generated EventName constants cannot imply browser behavior the runtime will never dispatch. req: convention/002 req: diagnostics/002
This commit is contained in:
+1
-1
@@ -864,7 +864,7 @@ All forms support returning `impl IntoEffect` and compose through tuples.
|
||||
001 slhx-axum and the JS runtime support common UX conventions as attributes, not core effects: `data-slhx-pending-class`, `data-slhx-indicator`, `data-slhx-confirm`, `data-slhx-debounce`, `data-slhx-throttle`, `data-slhx-every`, `data-slhx-disable-while-pending`, `data-slhx-policy`, and `data-slhx-on`. These are orthogonal to the core effect algebra.
|
||||
|
||||
### req: convention/002
|
||||
002 Default event triggers: `submit` for forms, `click` for buttons and links. `data-slhx-on` overrides the default.
|
||||
002 Default event triggers: `submit` for forms, `click` for buttons and links. `data-slhx-on` overrides the default for the runtime-supported delegated events: `click`, `submit`, `input`, `change`, `dragstart`, `dragover`, and `drop`. Unsupported static event names are build errors.
|
||||
|
||||
### req: convention/003
|
||||
003 `data-slhx-debounce` and `data-slhx-throttle` support simple millisecond values. No trigger mini-language in core.
|
||||
|
||||
+21
-3
@@ -1027,6 +1027,14 @@ fn reject_invalid_slhx_attr_values(path: &Path, attrs: &[SurfaceAttribute]) -> i
|
||||
"expected one of `latest`, `queue`, `drop`, or `parallel`",
|
||||
));
|
||||
}
|
||||
"data-slhx-on" if !event_tokens(value).all(valid_runtime_event) => {
|
||||
return Err(invalid_slhx_value(
|
||||
path,
|
||||
&attr.name,
|
||||
value,
|
||||
"expected runtime-supported events: `click`, `submit`, `input`, `change`, `dragstart`, `dragover`, or `drop`",
|
||||
));
|
||||
}
|
||||
"data-slhx-debounce" | "data-slhx-throttle" | "data-slhx-every"
|
||||
if !valid_duration(value) =>
|
||||
{
|
||||
@@ -1047,6 +1055,10 @@ fn valid_policy(value: &str) -> bool {
|
||||
matches!(value.trim(), "latest" | "queue" | "drop" | "parallel")
|
||||
}
|
||||
|
||||
fn valid_runtime_event(value: &str) -> bool {
|
||||
matches!(value, "click" | "submit" | "input" | "change" | "dragstart" | "dragover" | "drop")
|
||||
}
|
||||
|
||||
fn valid_duration(value: &str) -> bool {
|
||||
let value = value.trim();
|
||||
let digits = value
|
||||
@@ -1195,7 +1207,7 @@ mod tests {
|
||||
std::fs::create_dir_all(&templates).unwrap();
|
||||
std::fs::write(
|
||||
templates.join("todo.heml"),
|
||||
r#"<form data-slhx-handle="create" data-slhx-form="new_todo"><input name="title"></form><button data-slhx-handle="delete" data-todo-id="7" data-slhx-on="click keydown">Delete</button><ul data-slhx-slot="todos"></ul><section data-slhx-atom="filter"></section>"#,
|
||||
r#"<form data-slhx-handle="create" data-slhx-form="new_todo"><input name="title"></form><button data-slhx-handle="delete" data-todo-id="7" data-slhx-on="click change">Delete</button><ul data-slhx-slot="todos"></ul><section data-slhx-atom="filter"></section>"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -1215,7 +1227,7 @@ mod tests {
|
||||
assert!(generated.contains("pub const filter"));
|
||||
assert!(generated.contains("pub mod events"));
|
||||
assert!(generated.contains("pub const click: ::slhx::EventName = ::slhx::EventName::new(\"click\")"));
|
||||
assert!(generated.contains("pub const keydown: ::slhx::EventName = ::slhx::EventName::new(\"keydown\")"));
|
||||
assert!(generated.contains("pub const change: ::slhx::EventName = ::slhx::EventName::new(\"change\")"));
|
||||
assert!(generated.contains("pub const new_todo"));
|
||||
assert!(generated.contains("pub mod todo"));
|
||||
assert!(generated.contains("pub const ALL_IDS"));
|
||||
@@ -1233,7 +1245,7 @@ mod tests {
|
||||
assert!(syms.contains("handle_param\tdelete\ttodo_id\n"));
|
||||
assert!(syms.contains("event\t"));
|
||||
assert!(syms.contains("\tclick\tclick\n"));
|
||||
assert!(syms.contains("\tkeydown\tkeydown\n"));
|
||||
assert!(syms.contains("\tchange\tchange\n"));
|
||||
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
}
|
||||
@@ -1537,6 +1549,12 @@ fn main() {{
|
||||
"data-slhx-every",
|
||||
"1s",
|
||||
),
|
||||
(
|
||||
"event",
|
||||
r#"<button data-slhx-handle="save" data-slhx-on="keydown">Save</button>"#,
|
||||
"data-slhx-on",
|
||||
"click",
|
||||
),
|
||||
] {
|
||||
let base = test_dir(&format!("slhx-build-invalid-convention-{case}-test"));
|
||||
let templates = base.join("templates");
|
||||
|
||||
Reference in New Issue
Block a user