diff --git a/slhx-build/src/lib.rs b/slhx-build/src/lib.rs index d1f8961..43ceda1 100644 --- a/slhx-build/src/lib.rs +++ b/slhx-build/src/lib.rs @@ -332,7 +332,8 @@ fn __slhx_lower_html(html: &str, table: &[(&str, &str, u32)]) -> ::std::string:: }; out = __slhx_replace_attr(out, attr, name, runtime_attr, *id); } - out + out = __slhx_lower_static_attr(out, "data-slhx-key", "data-key"); + __slhx_inject_handle_inputs(out) } fn __slhx_replace_attr(mut html: ::std::string::String, attr: &str, name: &str, runtime_attr: &str, id: u32) -> ::std::string::String { @@ -342,6 +343,55 @@ fn __slhx_replace_attr(mut html: ::std::string::String, attr: &str, name: &str, let single = ::std::format!("{attr}='{name}'"); html.replace(&single, &replacement) } + +fn __slhx_lower_static_attr(mut html: ::std::string::String, attr: &str, runtime_attr: &str) -> ::std::string::String { + html = html.replace(&::std::format!("{attr}=\""), &::std::format!("{runtime_attr}=\"")); + html.replace(&::std::format!("{attr}='"), &::std::format!("{runtime_attr}='")) +} + +fn __slhx_inject_handle_inputs(html: ::std::string::String) -> ::std::string::String { + let mut out = ::std::string::String::with_capacity(html.len()); + let mut rest = html.as_str(); + while let Some(start) = rest.find("
").unwrap_or(rest.len()); + let form_body = &rest[..form_body_end]; + if !__slhx_has_handle_input(form_body) { + out.push_str(&::std::format!("", handle_id)); + } + } + out.push_str(rest); + out +} + +fn __slhx_has_handle_input(html: &str) -> bool { + html.contains("name=\"__h\"") || html.contains("name='__h'") +} + +fn __slhx_attr(tag: &str, attr: &str) -> Option<::std::string::String> { + let attr_at = tag.find(attr)?; + let after_attr = &tag[attr_at + attr.len()..]; + let after_equals = after_attr.trim_start().strip_prefix('=')?.trim_start(); + let quote = after_equals.chars().next()?; + if quote != '\"' && quote != '\'' { + return None; + } + let value = &after_equals[quote.len_utf8()..]; + let end = value.find(quote)?; + Some(value[..end].to_owned()) +} "#); } @@ -660,6 +710,7 @@ mod tests { assert!(generated.contains("data-slhx-form")); assert!(generated.contains("data-sid")); assert!(generated.contains("data-fid")); + assert!(generated.contains("__slhx_inject_handle_inputs")); let syms = std::fs::read_to_string(out.join("slhx.syms")).unwrap(); assert!(syms.contains("atom\t")); @@ -688,6 +739,69 @@ mod tests { let _ = std::fs::remove_dir_all(&base); } + #[test] + fn generated_lowering_injects_progressive_form_handle_and_key() { + // req: form/002, req: wire/001 + let base = test_dir("slhx-build-lowering-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("todo.heml"), + r#""#, + ) + .unwrap(); + + app().template_dir(&templates).out_dir(&out).run().unwrap(); + let generated = std::fs::read_to_string(out.join("slhx.generated.rs")).unwrap(); + assert!(generated.contains("fn __slhx_lower_html")); + let source = format!( + r###" +#![allow(dead_code)] +mod slhx {{ + #[derive(Clone, Copy)] pub struct BuildFingerprint; + impl BuildFingerprint {{ pub const fn from_parts(_: &[u32]) -> Self {{ Self }} }} + #[derive(Clone, Copy)] pub struct Slot