diff --git a/slhx-build/src/lib.rs b/slhx-build/src/lib.rs
index b19a664..94dc7ed 100644
--- a/slhx-build/src/lib.rs
+++ b/slhx-build/src/lib.rs
@@ -149,6 +149,8 @@ impl Resources {
continue;
};
+ reject_selector_target_attrs(path, &node.attrs)?;
+
if let Some(class_attr) = static_attr(&node.attrs, "class") {
for token in class_tokens(&class_attr) {
let canonical = canonical_symbol(root, path, token);
@@ -950,6 +952,22 @@ fn is_inside_keyed_for(surface: &SurfaceDocument, mut scope: ScopeId) -> bool {
}
}
+fn reject_selector_target_attrs(path: &Path, attrs: &[SurfaceAttribute]) -> io::Result<()> {
+ for attr in attrs {
+ let name = attr.name.as_str();
+ if matches!(name, "data-slhx-target" | "data-slhx-select") {
+ return Err(io::Error::new(
+ io::ErrorKind::InvalidData,
+ format!(
+ "{}: `{name}` is selector-style targeting; slhx uses generated resources instead. Add data-slhx-slot to the local element and return an effect for that generated slot.",
+ path.display()
+ ),
+ ));
+ }
+ }
+ Ok(())
+}
+
fn reject_unkeyed_loop(
surface: &SurfaceDocument,
mut scope: ScopeId,
@@ -1345,6 +1363,37 @@ fn main() {{
}
}
+ #[test]
+ fn rejects_selector_style_targeting_attrs() {
+ // req: locality/001 req: locality/002 req: htmx_equivalents/003
+ for (case, template, attr) in [
+ (
+ "slhx-target",
+ r##""##,
+ "data-slhx-target",
+ ),
+ (
+ "slhx-select",
+ r##""##,
+ "data-slhx-select",
+ ),
+ ] {
+ let base = test_dir(&format!("slhx-build-selector-target-{case}-test"));
+ let templates = base.join("templates");
+ let out = base.join("out");
+ let _ = std::fs::remove_dir_all(&base);
+ std::fs::create_dir_all(&templates).unwrap();
+ std::fs::write(templates.join("target.heml"), template).unwrap();
+
+ let err = app().template_dir(&templates).out_dir(&out).run().unwrap_err();
+ assert!(err.to_string().contains(attr), "missing attr in diagnostic: {err}");
+ assert!(err.to_string().contains("selector-style targeting"), "missing selector diagnostic: {err}");
+ assert!(err.to_string().contains("data-slhx-slot"), "missing suggested slot fix: {err}");
+
+ let _ = std::fs::remove_dir_all(&base);
+ }
+ }
+
#[test]
fn generated_lowering_injects_progressive_form_handle_and_key() {
// req: form/002, req: wire/001