Repository: ahecha-co/ahecha (language: rust)

Issue:
Rework `page` to support render as partial
Use case, to render just the inner page (as a partial) without the layout and document, useful to embed the pages if needed.


Discussion / hints:

Fix patch:
diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml
index dd87335..344dd1d 100644
--- a/core/codegen/Cargo.toml
+++ b/core/codegen/Cargo.toml
@@ -12,12 +12,10 @@ frontend = []
 proc-macro=true
 
 [dependencies]
-ahecha_tuple_list={ path = "../tuple_list", version = "*" }
 convert_case="0.4.0"
 proc-macro2="1.0.32"
 proc-macro-error="1.0.4"
 quote="1.0.9"
-regex="1.5.4"
 syn={version="1.0.81",features=["full"]}
 
 [depencencies.web-sys]
diff --git a/core/codegen/src/custom_element.rs b/core/codegen/src/custom_element.rs
index 841a931..2d90bde 100644
--- a/core/codegen/src/custom_element.rs
+++ b/core/codegen/src/custom_element.rs
@@ -1,4 +1,5 @@
 use proc_macro::TokenStream;
+use proc_macro_error::emit_error;
 use quote::quote;
 
 use crate::utils::FnStruct;
@@ -16,6 +17,23 @@ pub fn create_custom_element(f: syn::ItemFn) -> TokenStream {
 
   let input_readings = fn_struct.input_readings();
 
+  let struct_str_name = struct_name.to_string();
+  if struct_str_name.to_uppercase().chars().next().unwrap()
+    != struct_str_name.chars().next().unwrap()
+  {
+    emit_error!(
+      struct_name.span(),
+      "Custom elements must start with a upper letter"
+    );
+  }
+
+  if struct_str_name.ends_with("Partial") || struct_str_name.ends_with("Page") {
+    emit_error!(
+      struct_name.span(),
+      "Custom elements cannot end with `Partial` or `Page` suffix.",
+    );
+  }
+
   quote! {
     #[derive(Debug)]
     #vis struct #struct_name #impl_generics #input_blocks
diff --git a/core/codegen/src/html/node.rs b/core/codegen/src/html/node.rs
index 1f23eb3..fa5d0dc 100644
--- a/core/codegen/src/html/node.rs
+++ b/core/codegen/src/html/node.rs
@@ -105,7 +105,7 @@ impl Parse for HtmlNode {
       };
 
       if name.to_string().chars().next().unwrap().is_uppercase() {
-        if name.to_string().ends_with("Partial") {
+        if name.to_string().ends_with("Partial") || name.to_string().ends_with("Page") {
           Ok(HtmlNode::Partial(HtmlPartial {
             attributes,
             children,
diff --git a/core/codegen/src/page.rs b/core/codegen/src/page.rs
index 042bef0..e99df90 100644
--- a/core/codegen/src/page.rs
+++ b/core/codegen/src/page.rs
@@ -5,6 +5,7 @@ use syn::AttributeArgs;
 
 use crate::{
   page::attributes::PageAttributes,
+  partial::create_partial_internal,
   routes::{generate_route_path, RouteType},
   utils::FnStruct,
 };
@@ -14,12 +15,10 @@ mod attributes;
 pub fn create_page(f: syn::ItemFn, attrs: AttributeArgs) -> TokenStream {
   let fn_struct: FnStruct = f.into();
 
-  let vis = fn_struct.vis();
   let struct_name = fn_struct.name();
   let impl_generics = fn_struct.impl_generics();
   let ty_generics = fn_struct.type_generics();
   let where_clause = fn_struct.where_clause();
-  let input_blocks = fn_struct.input_blocks();
   let input_fields = fn_struct.input_fields();
   let block = fn_struct.block();
 
@@ -52,8 +51,10 @@ pub fn create_page(f: syn::ItemFn, attrs: AttributeArgs) -> TokenStream {
   let uri = route.build_uri();
   let uri_input_fields = route.params();
 
+  let partial = create_partial_internal(&fn_struct, false);
+
   quote! {
-    #vis struct #struct_name #impl_generics #input_blocks
+    #partial
 
     impl #ty_generics #struct_name #impl_generics #where_clause {
       pub fn uri(#uri_input_fields) -> String {
diff --git a/core/codegen/src/partial.rs b/core/codegen/src/partial.rs
index 3e08689..a7e04c4 100644
--- a/core/codegen/src/partial.rs
+++ b/core/codegen/src/partial.rs
@@ -4,9 +4,10 @@ use quote::quote;
 
 use crate::utils::FnStruct;
 
-pub fn create_partial(f: syn::ItemFn) -> TokenStream {
-  let fn_struct: FnStruct = f.into();
-
+pub(crate) fn create_partial_internal(
+  fn_struct: &FnStruct,
+  is_partial: bool,
+) -> proc_macro2::TokenStream {
   let vis = fn_struct.vis();
   let struct_name = fn_struct.name();
   let block = fn_struct.block();
@@ -17,22 +18,24 @@ pub fn create_partial(f: syn::ItemFn) -> TokenStream {
 
   let input_readings = fn_struct.input_readings();
 
-  let struct_str_name = struct_name.to_string();
-  if struct_str_name.to_uppercase().chars().next().unwrap()
-    != struct_str_name.chars().next().unwrap()
-  {
-    emit_error!(
-      struct_name.span(),
-      "Partials must start with a upper letter"
-    );
-  }
+  if is_partial {
+    let struct_str_name = struct_name.to_string();
+    if struct_str_name.to_uppercase().chars().next().unwrap()
+      != struct_str_name.chars().next().unwrap()
+    {
+      emit_error!(
+        struct_name.span(),
+        "Partials must start with a upper letter"
+      );
+    }
 
-  if !struct_str_name.ends_with("Partial") {
-    emit_error!(
-      struct_name.span(),
-      "Partials must have the `Partial` suffix, example: `{}Partial`",
-      struct_str_name
-    );
+    if !struct_str_name.ends_with("Partial") {
+      emit_error!(
+        struct_name.span(),
+        "Partials must have the `Partial` suffix, example: `{}Partial`",
+        struct_str_name
+      );
+    }
   }
 
   quote! {
@@ -52,9 +55,14 @@ pub fn create_partial(f: syn::ItemFn) -> TokenStream {
 
     impl #impl_generics Into<String> for #struct_name #ty_generics #where_clause {
       fn into(self) -> String {
+        use ahecha::view::Render;
         self.render()
       }
     }
   }
-  .into()
+}
+
+pub fn create_partial(f: syn::ItemFn) -> TokenStream {
+  let fn_struct = f.into();
+  create_partial_internal(&fn_struct, true).into()
 }


Test patch:
diff --git a/core/codegen/tests/page.rs b/core/codegen/tests/page.rs
index abb4429..37e01bd 100644
--- a/core/codegen/tests/page.rs
+++ b/core/codegen/tests/page.rs
@@ -1,3 +1,4 @@
+use ahecha_codegen::html;
 use ahecha_view::Render;
 use app::page;
 
@@ -30,3 +31,19 @@ fn test_test_page_request() {
     "<html><head><title>Document title</title></head><body><div>Test page<span>5</span></div></body></html>"
   );
 }
+
+#[test]
+fn test_page_as_partial() {
+  use page::__count__::CountPage;
+
+  let res = html!(
+    <div>
+      <CountPage count=5 />
+    </div>
+  );
+
+  assert_eq!(
+    res.render(),
+    "<div><div>Test page<span>5</span></div></div>"
+  );
+}