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.
|
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
|
### 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
|
### req: convention/003
|
||||||
003 `data-slhx-debounce` and `data-slhx-throttle` support simple millisecond values. No trigger mini-language in core.
|
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`",
|
"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"
|
"data-slhx-debounce" | "data-slhx-throttle" | "data-slhx-every"
|
||||||
if !valid_duration(value) =>
|
if !valid_duration(value) =>
|
||||||
{
|
{
|
||||||
@@ -1047,6 +1055,10 @@ fn valid_policy(value: &str) -> bool {
|
|||||||
matches!(value.trim(), "latest" | "queue" | "drop" | "parallel")
|
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 {
|
fn valid_duration(value: &str) -> bool {
|
||||||
let value = value.trim();
|
let value = value.trim();
|
||||||
let digits = value
|
let digits = value
|
||||||
@@ -1195,7 +1207,7 @@ mod tests {
|
|||||||
std::fs::create_dir_all(&templates).unwrap();
|
std::fs::create_dir_all(&templates).unwrap();
|
||||||
std::fs::write(
|
std::fs::write(
|
||||||
templates.join("todo.heml"),
|
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();
|
.unwrap();
|
||||||
|
|
||||||
@@ -1215,7 +1227,7 @@ mod tests {
|
|||||||
assert!(generated.contains("pub const filter"));
|
assert!(generated.contains("pub const filter"));
|
||||||
assert!(generated.contains("pub mod events"));
|
assert!(generated.contains("pub mod events"));
|
||||||
assert!(generated.contains("pub const click: ::slhx::EventName = ::slhx::EventName::new(\"click\")"));
|
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 const new_todo"));
|
||||||
assert!(generated.contains("pub mod todo"));
|
assert!(generated.contains("pub mod todo"));
|
||||||
assert!(generated.contains("pub const ALL_IDS"));
|
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("handle_param\tdelete\ttodo_id\n"));
|
||||||
assert!(syms.contains("event\t"));
|
assert!(syms.contains("event\t"));
|
||||||
assert!(syms.contains("\tclick\tclick\n"));
|
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);
|
let _ = std::fs::remove_dir_all(&base);
|
||||||
}
|
}
|
||||||
@@ -1537,6 +1549,12 @@ fn main() {{
|
|||||||
"data-slhx-every",
|
"data-slhx-every",
|
||||||
"1s",
|
"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 base = test_dir(&format!("slhx-build-invalid-convention-{case}-test"));
|
||||||
let templates = base.join("templates");
|
let templates = base.join("templates");
|
||||||
|
|||||||
Reference in New Issue
Block a user