fix(build): dispatch slot targets to matching component render/put
req: runtime/005 req: examples/001 req: htmx_equivalents/001
This commit is contained in:
Generated
+1
@@ -618,6 +618,7 @@ dependencies = [
|
|||||||
name = "hemx-test"
|
name = "hemx-test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"hemx-build",
|
||||||
"hemx-core",
|
"hemx-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -739,7 +739,12 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.batch,
|
.batch,
|
||||||
);
|
);
|
||||||
assert!(edit.updates_html_containing(gallery::contact_card, "save_contact"));
|
// Component-level rendering now lowers contact_card-local handles, so the
|
||||||
|
// effect contains the lowered handle id, not the source name.
|
||||||
|
assert!(edit.updates_html_containing(
|
||||||
|
gallery::contact_card,
|
||||||
|
&format!("data-hid=\"{}\"", contact_card::save_contact),
|
||||||
|
));
|
||||||
|
|
||||||
let save = inspect_batch(
|
let save = inspect_batch(
|
||||||
InteractionRequest::from(form(
|
InteractionRequest::from(form(
|
||||||
|
|||||||
+76
-22
@@ -539,16 +539,20 @@ impl Resources {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// req: build/001 req: component/003
|
// req: build/001 req: component/003
|
||||||
|
let component_names: BTreeSet<String> = self.component_names().into_iter().collect();
|
||||||
|
|
||||||
if global_exports {
|
if global_exports {
|
||||||
self.push_component_refs(&mut out, 0);
|
self.push_component_refs(&mut out, 0);
|
||||||
self.push_resource_modules(&mut out, None, 0);
|
self.push_resource_modules(&mut out, &component_names, None, 0);
|
||||||
}
|
}
|
||||||
self.push_lowering_api(&mut out, None, 0);
|
self.push_lowering_api(&mut out, None, 0);
|
||||||
|
|
||||||
for component in self.component_names() {
|
for component in self.component_names() {
|
||||||
out.push_str("\n#[allow(non_upper_case_globals)]\n");
|
out.push_str("\n#[allow(non_upper_case_globals)]\n");
|
||||||
out.push_str(&format!("pub mod {component} {{\n"));
|
out.push_str(&format!("pub mod {component} {{\n"));
|
||||||
self.push_resource_modules(&mut out, Some(&component), 1);
|
out.push_str(" #[derive(Clone, Copy)]\n");
|
||||||
|
out.push_str(" pub struct Component;\n\n");
|
||||||
|
self.push_resource_modules(&mut out, &component_names, Some(&component), 1);
|
||||||
self.push_lowering_api(&mut out, Some(&component), 1);
|
self.push_lowering_api(&mut out, Some(&component), 1);
|
||||||
out.push_str("}\n");
|
out.push_str("}\n");
|
||||||
}
|
}
|
||||||
@@ -572,7 +576,13 @@ impl Resources {
|
|||||||
out.push_str(&format!("{pad}}}\n"));
|
out.push_str(&format!("{pad}}}\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_resource_modules(&self, out: &mut String, component: Option<&str>, indent: usize) {
|
fn push_resource_modules(
|
||||||
|
&self,
|
||||||
|
out: &mut String,
|
||||||
|
components: &BTreeSet<String>,
|
||||||
|
component: Option<&str>,
|
||||||
|
indent: usize,
|
||||||
|
) {
|
||||||
let pad = " ".repeat(indent);
|
let pad = " ".repeat(indent);
|
||||||
let inner = " ".repeat(indent + 1);
|
let inner = " ".repeat(indent + 1);
|
||||||
let mut handle_ids = Vec::new();
|
let mut handle_ids = Vec::new();
|
||||||
@@ -613,34 +623,53 @@ impl Resources {
|
|||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{pad}#[allow(non_upper_case_globals)]\n{pad}pub mod targets {{\n"
|
"{pad}#[allow(non_upper_case_globals)]\n{pad}pub mod targets {{\n"
|
||||||
));
|
));
|
||||||
|
let child_prefix = if component.is_some() {
|
||||||
|
"super::super::"
|
||||||
|
} else {
|
||||||
|
"super::"
|
||||||
|
};
|
||||||
out.push_str(&format!("{inner}#[derive(Clone, Copy)]\n"));
|
out.push_str(&format!("{inner}#[derive(Clone, Copy)]\n"));
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}pub struct SlotTarget<T> {{ slot: ::hemx::advanced::Slot<T> }}\n"
|
"{inner}pub struct SlotTarget<T, C = ()> {{ slot: ::hemx::advanced::Slot<T>, _marker: ::std::marker::PhantomData<C> }}\n"
|
||||||
));
|
));
|
||||||
out.push_str(&format!("{inner}#[allow(dead_code)]\n"));
|
out.push_str(&format!("{inner}#[allow(dead_code)]\n"));
|
||||||
out.push_str(&format!("{inner}impl<T> SlotTarget<T> {{\n"));
|
out.push_str(&format!("{inner}impl<T, C> SlotTarget<T, C> {{\n"));
|
||||||
out.push_str(&format!("{inner} const fn new(slot: ::hemx::advanced::Slot<T>) -> Self {{ Self {{ slot }} }}\n"));
|
out.push_str(&format!("{inner} const fn new(slot: ::hemx::advanced::Slot<T>) -> Self {{ Self {{ slot, _marker: ::std::marker::PhantomData }} }}\n"));
|
||||||
out.push_str(&format!("{inner} pub fn put(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ super::put(self.slot, view) }}\n"));
|
|
||||||
out.push_str(&format!("{inner} pub fn replace(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ super::put(self.slot, view) }}\n"));
|
|
||||||
out.push_str(&format!("{inner} pub fn text(self, value: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.text(value) }}\n"));
|
out.push_str(&format!("{inner} pub fn text(self, value: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.text(value) }}\n"));
|
||||||
out.push_str(&format!("{inner} pub fn set(self, value: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.text(value) }}\n"));
|
out.push_str(&format!("{inner} pub fn set(self, value: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.text(value) }}\n"));
|
||||||
out.push_str(&format!("{inner}}}\n"));
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
|
out.push_str(&format!("{inner}impl<T> SlotTarget<T, ()> {{\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn put(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ super::put(self.slot, view) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn replace(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ super::put(self.slot, view) }}\n"));
|
||||||
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
|
for res in self
|
||||||
|
.slots
|
||||||
|
.values()
|
||||||
|
.filter(|res| component_matches(res, component))
|
||||||
|
.filter(|res| components.contains(&res.ident) && Some(res.ident.as_str()) != component)
|
||||||
|
{
|
||||||
|
let child = &res.ident;
|
||||||
|
out.push_str(&format!("{inner}impl<T> SlotTarget<T, {child_prefix}{child}::Component> {{\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn put(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ {child_prefix}{child}::put(self.slot, view) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn replace(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ {child_prefix}{child}::put(self.slot, view) }}\n"));
|
||||||
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
|
}
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}impl<T> ::hemx::GeneratedTarget for SlotTarget<T> {{\n"
|
"{inner}impl<T, C> ::hemx::GeneratedTarget for SlotTarget<T, C> {{\n"
|
||||||
));
|
));
|
||||||
out.push_str(&format!("{inner} fn __hemx_resource_id(self) -> ::hemx::advanced::ResourceId {{ self.slot.id() }}\n"));
|
out.push_str(&format!("{inner} fn __hemx_resource_id(self) -> ::hemx::advanced::ResourceId {{ self.slot.id() }}\n"));
|
||||||
out.push_str(&format!("{inner}}}\n"));
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
out.push_str(&format!("{inner}#[derive(Clone, Copy)]\n"));
|
out.push_str(&format!("{inner}#[derive(Clone, Copy)]\n"));
|
||||||
out.push_str(&format!("{inner}pub struct KeyedSlotTarget<K, T> {{ slot: ::hemx::advanced::KeyedSlot<K, T> }}\n"));
|
out.push_str(&format!("{inner}pub struct KeyedSlotTarget<K, T, C = ()> {{ slot: ::hemx::advanced::KeyedSlot<K, T>, _marker: ::std::marker::PhantomData<C> }}\n"));
|
||||||
out.push_str(&format!("{inner}#[allow(dead_code)]\n"));
|
out.push_str(&format!("{inner}#[allow(dead_code)]\n"));
|
||||||
out.push_str(&format!("{inner}impl<K, T> KeyedSlotTarget<K, T>\n"));
|
out.push_str(&format!("{inner}impl<K, T, C> KeyedSlotTarget<K, T, C>\n"));
|
||||||
out.push_str(&format!("{inner}where\n"));
|
out.push_str(&format!("{inner}where\n"));
|
||||||
out.push_str(&format!("{inner} K: ::std::string::ToString,\n"));
|
out.push_str(&format!("{inner} K: ::std::string::ToString,\n"));
|
||||||
out.push_str(&format!("{inner}{{\n"));
|
out.push_str(&format!("{inner}{{\n"));
|
||||||
out.push_str(&format!("{inner} const fn new(slot: ::hemx::advanced::KeyedSlot<K, T>) -> Self {{ Self {{ slot }} }}\n"));
|
out.push_str(&format!("{inner} const fn new(slot: ::hemx::advanced::KeyedSlot<K, T>) -> Self {{ Self {{ slot, _marker: ::std::marker::PhantomData }} }}\n"));
|
||||||
out.push_str(&format!("{inner}}}\n"));
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}impl<T> KeyedSlotTarget<::std::string::String, T> {{\n"
|
"{inner}impl<T> KeyedSlotTarget<::std::string::String, T, ()> {{\n"
|
||||||
));
|
));
|
||||||
out.push_str(&format!("{inner} pub fn append(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.append_html(view.hemx_key(), super::render(&view)) }}\n"));
|
out.push_str(&format!("{inner} pub fn append(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.append_html(view.hemx_key(), super::render(&view)) }}\n"));
|
||||||
out.push_str(&format!("{inner} pub fn prepend(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.prepend_html(view.hemx_key(), super::render(&view)) }}\n"));
|
out.push_str(&format!("{inner} pub fn prepend(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.prepend_html(view.hemx_key(), super::render(&view)) }}\n"));
|
||||||
@@ -650,8 +679,28 @@ impl Resources {
|
|||||||
out.push_str(&format!("{inner} pub fn replace_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ self.slot.replace_html(key.to_string(), super::render(view)) }}\n"));
|
out.push_str(&format!("{inner} pub fn replace_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ self.slot.replace_html(key.to_string(), super::render(view)) }}\n"));
|
||||||
out.push_str(&format!("{inner} pub fn remove(self, key: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.remove(key.to_string()) }}\n"));
|
out.push_str(&format!("{inner} pub fn remove(self, key: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.remove(key.to_string()) }}\n"));
|
||||||
out.push_str(&format!("{inner}}}\n"));
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
|
for res in self
|
||||||
|
.slots
|
||||||
|
.values()
|
||||||
|
.filter(|res| component_matches(res, component))
|
||||||
|
.filter(|res| res.keyed)
|
||||||
|
.filter(|res| components.contains(&res.ident) && Some(res.ident.as_str()) != component)
|
||||||
|
{
|
||||||
|
let child = &res.ident;
|
||||||
|
out.push_str(&format!(
|
||||||
|
"{inner}impl<T> KeyedSlotTarget<::std::string::String, T, {child_prefix}{child}::Component> {{\n"
|
||||||
|
));
|
||||||
|
out.push_str(&format!("{inner} pub fn append(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.append_html(view.hemx_key(), {child_prefix}{child}::render(&view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn prepend(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.prepend_html(view.hemx_key(), {child_prefix}{child}::render(&view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn replace(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect {{ self.slot.replace_html(view.hemx_key(), {child_prefix}{child}::render(&view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn append_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ self.slot.append_html(key.to_string(), {child_prefix}{child}::render(view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn prepend_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ self.slot.prepend_html(key.to_string(), {child_prefix}{child}::render(view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn replace_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect {{ self.slot.replace_html(key.to_string(), {child_prefix}{child}::render(view)) }}\n"));
|
||||||
|
out.push_str(&format!("{inner} pub fn remove(self, key: impl ::std::string::ToString) -> ::hemx::advanced::Effect {{ self.slot.remove(key.to_string()) }}\n"));
|
||||||
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
|
}
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}impl<K: ::std::string::ToString, T> ::hemx::GeneratedTarget for KeyedSlotTarget<K, T> {{\n"
|
"{inner}impl<K: ::std::string::ToString, T, C> ::hemx::GeneratedTarget for KeyedSlotTarget<K, T, C> {{\n"
|
||||||
));
|
));
|
||||||
out.push_str(&format!("{inner} fn __hemx_resource_id(self) -> ::hemx::advanced::ResourceId {{ self.slot.id() }}\n"));
|
out.push_str(&format!("{inner} fn __hemx_resource_id(self) -> ::hemx::advanced::ResourceId {{ self.slot.id() }}\n"));
|
||||||
out.push_str(&format!("{inner}}}\n"));
|
out.push_str(&format!("{inner}}}\n"));
|
||||||
@@ -660,14 +709,19 @@ impl Resources {
|
|||||||
.values()
|
.values()
|
||||||
.filter(|res| component_matches(res, component))
|
.filter(|res| component_matches(res, component))
|
||||||
{
|
{
|
||||||
|
let marker = if components.contains(&res.ident) && Some(res.ident.as_str()) != component {
|
||||||
|
format!("{child_prefix}{}::Component", res.ident)
|
||||||
|
} else {
|
||||||
|
"()".to_string()
|
||||||
|
};
|
||||||
if res.keyed {
|
if res.keyed {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}pub const {}: KeyedSlotTarget<::std::string::String, ::std::string::String> = KeyedSlotTarget::new(super::advanced::slots::{});\n",
|
"{inner}pub const {}: KeyedSlotTarget<::std::string::String, ::std::string::String, {marker}> = KeyedSlotTarget::new(super::advanced::slots::{});\n",
|
||||||
res.ident, res.ident
|
res.ident, res.ident
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
out.push_str(&format!(
|
out.push_str(&format!(
|
||||||
"{inner}pub const {}: SlotTarget<::std::string::String> = SlotTarget::new(super::advanced::slots::{});\n",
|
"{inner}pub const {}: SlotTarget<::std::string::String, {marker}> = SlotTarget::new(super::advanced::slots::{});\n",
|
||||||
res.ident, res.ident
|
res.ident, res.ident
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -2127,10 +2181,10 @@ mod tests {
|
|||||||
assert!(generated.contains("pub mod slots"));
|
assert!(generated.contains("pub mod slots"));
|
||||||
assert!(generated.contains("pub const todos"));
|
assert!(generated.contains("pub const todos"));
|
||||||
assert!(generated.contains("pub mod targets"));
|
assert!(generated.contains("pub mod targets"));
|
||||||
assert!(generated.contains("pub struct SlotTarget<T>"));
|
assert!(generated.contains("pub struct SlotTarget<T, C = ()>"));
|
||||||
assert!(generated.contains("impl<T> ::hemx::GeneratedTarget for SlotTarget<T>"));
|
assert!(generated.contains("impl<T, C> ::hemx::GeneratedTarget for SlotTarget<T, C>"));
|
||||||
assert!(generated.contains(
|
assert!(generated.contains(
|
||||||
"impl<K: ::std::string::ToString, T> ::hemx::GeneratedTarget for KeyedSlotTarget<K, T>"
|
"impl<K: ::std::string::ToString, T, C> ::hemx::GeneratedTarget for KeyedSlotTarget<K, T, C>"
|
||||||
));
|
));
|
||||||
assert!(generated.contains(
|
assert!(generated.contains(
|
||||||
"pub fn append(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect"
|
"pub fn append(self, view: impl ::hemplate::Hemplate + ::hemx::KeyedPartial) -> ::hemx::advanced::Effect"
|
||||||
@@ -2138,7 +2192,7 @@ mod tests {
|
|||||||
assert!(generated.contains(
|
assert!(generated.contains(
|
||||||
"pub fn append_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect"
|
"pub fn append_keyed(self, key: impl ::std::string::ToString, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect"
|
||||||
));
|
));
|
||||||
assert!(generated.contains("pub const todos: SlotTarget<::std::string::String> = SlotTarget::new(super::advanced::slots::todos);"));
|
assert!(generated.contains("pub const todos: SlotTarget<::std::string::String, ()> = SlotTarget::new(super::advanced::slots::todos);"));
|
||||||
assert!(generated.contains("pub use self::targets::todos;"));
|
assert!(generated.contains("pub use self::targets::todos;"));
|
||||||
assert!(generated.contains(
|
assert!(generated.contains(
|
||||||
"pub fn put(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect"
|
"pub fn put(self, view: &impl ::hemplate::Hemplate) -> ::hemx::advanced::Effect"
|
||||||
@@ -2253,7 +2307,7 @@ mod tests {
|
|||||||
assert!(generated.contains("pub mod todo"));
|
assert!(generated.contains("pub mod todo"));
|
||||||
assert!(generated.contains("pub const create: ::hemx::Handle<()> = ::hemx::Handle::new("));
|
assert!(generated.contains("pub const create: ::hemx::Handle<()> = ::hemx::Handle::new("));
|
||||||
assert!(generated.contains("pub const todos: ::hemx::advanced::Slot<::std::string::String> = ::hemx::advanced::Slot::new("));
|
assert!(generated.contains("pub const todos: ::hemx::advanced::Slot<::std::string::String> = ::hemx::advanced::Slot::new("));
|
||||||
assert!(generated.contains("pub const todos: SlotTarget<::std::string::String> = SlotTarget::new(super::advanced::slots::todos);"));
|
assert!(generated.contains("pub const todos: SlotTarget<::std::string::String, ()> = SlotTarget::new(super::advanced::slots::todos);"));
|
||||||
assert!(generated.contains("pub use self::targets::todos;"));
|
assert!(generated.contains("pub use self::targets::todos;"));
|
||||||
assert!(generated
|
assert!(generated
|
||||||
.contains("pub const click: ::hemx::EventName = ::hemx::EventName::new(\"click\")"));
|
.contains("pub const click: ::hemx::EventName = ::hemx::EventName::new(\"click\")"));
|
||||||
@@ -2301,7 +2355,7 @@ mod tests {
|
|||||||
assert!(generated.contains(" pub mod advanced"));
|
assert!(generated.contains(" pub mod advanced"));
|
||||||
assert!(generated.contains(" pub mod slots"));
|
assert!(generated.contains(" pub mod slots"));
|
||||||
assert!(generated.contains(" pub mod targets"));
|
assert!(generated.contains(" pub mod targets"));
|
||||||
assert!(generated.contains(" pub const todos: SlotTarget<::std::string::String> = SlotTarget::new(super::advanced::slots::todos);"));
|
assert!(generated.contains(" pub const todos: SlotTarget<::std::string::String, ()> = SlotTarget::new(super::advanced::slots::todos);"));
|
||||||
assert!(generated.contains(" pub use self::targets::todos;"));
|
assert!(generated.contains(" pub use self::targets::todos;"));
|
||||||
assert!(generated.contains(" #[doc(hidden)]\n pub fn lower_html"));
|
assert!(generated.contains(" #[doc(hidden)]\n pub fn lower_html"));
|
||||||
assert!(generated.contains(
|
assert!(generated.contains(
|
||||||
|
|||||||
@@ -5,3 +5,6 @@ edition.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hemx-core = { path = "../hemx-core" }
|
hemx-core = { path = "../hemx-core" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hemx-build = { path = "../hemx-build" }
|
||||||
|
|||||||
@@ -278,6 +278,51 @@ fn browser_e2e_does_not_shortcut_product_interactions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn html_examples_slot_targets_dispatch_to_component_put() {
|
||||||
|
// req: runtime/005 req: examples/001 req: htmx_equivalents/001
|
||||||
|
// Regression: slots named after a component (e.g. contact_card, editable_row)
|
||||||
|
// must lower injected partials using that component's handle table, not the
|
||||||
|
// containing page's handle table.
|
||||||
|
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
||||||
|
let template_dir = root.join("examples/html_examples/templates");
|
||||||
|
let out_dir = std::env::temp_dir().join(format!(
|
||||||
|
"hemx-test-html-examples-slot-targets-{}",
|
||||||
|
std::process::id()
|
||||||
|
));
|
||||||
|
|
||||||
|
let _ = std::fs::remove_dir_all(&out_dir);
|
||||||
|
std::fs::create_dir_all(&out_dir).unwrap();
|
||||||
|
|
||||||
|
let result = std::panic::catch_unwind(|| {
|
||||||
|
hemx_build::app()
|
||||||
|
.template_dir(&template_dir)
|
||||||
|
.out_dir(&out_dir)
|
||||||
|
.run()
|
||||||
|
.expect("hemx-build should generate html_examples artifacts");
|
||||||
|
|
||||||
|
let generated =
|
||||||
|
std::fs::read_to_string(out_dir.join("hemx.generated.rs")).unwrap_or_default();
|
||||||
|
|
||||||
|
for component in ["contact_card", "editable_row"] {
|
||||||
|
let component_path = format!("super::super::{component}");
|
||||||
|
let impl_header = format!("impl<T> SlotTarget<T, {component_path}::Component>");
|
||||||
|
let put_call = format!("{component_path}::put(self.slot, view)");
|
||||||
|
assert!(
|
||||||
|
generated.contains(&impl_header),
|
||||||
|
"gallery::targets should have a component-specific SlotTarget impl for `{component}`; missing `{impl_header}`"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
generated.contains(&put_call),
|
||||||
|
"gallery::targets::{component} put/replace should dispatch to `{component_path}::put`; missing `{put_call}`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = std::fs::remove_dir_all(&out_dir);
|
||||||
|
result.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn scan_examples(dir: &Path, visit: &mut impl FnMut(&Path, &str)) {
|
fn scan_examples(dir: &Path, visit: &mut impl FnMut(&Path, &str)) {
|
||||||
for entry in std::fs::read_dir(dir).unwrap() {
|
for entry in std::fs::read_dir(dir).unwrap() {
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user