diff --git a/examples/showcase/app/src/bin/build.rs b/examples/showcase/app/src/bin/build.rs index 90ef8f3603..2529ca21fc 100644 --- a/examples/showcase/app/src/bin/build.rs +++ b/examples/showcase/app/src/bin/build.rs @@ -1,6 +1,6 @@ use perseus::{ config_manager::{FsConfigManager, ConfigManager}, - build_pages + build_templates }; use perseus_showcase_app::pages; use sycamore::prelude::SsrNode; @@ -8,7 +8,7 @@ use sycamore::prelude::SsrNode; fn main() { let config_manager = FsConfigManager::new(); - build_pages!([ + build_templates!([ pages::index::get_page::(), pages::about::get_page::(), pages::post::get_page::() diff --git a/examples/showcase/app/src/pages/about.rs b/examples/showcase/app/src/pages/about.rs index c3100a83d6..4ff6f249f3 100644 --- a/examples/showcase/app/src/pages/about.rs +++ b/examples/showcase/app/src/pages/about.rs @@ -1,15 +1,15 @@ -use sycamore::prelude::*; -use perseus::page::Page; +use sycamore::prelude::{template, component, GenericNode, Template as SycamoreTemplate}; +use perseus::template::Template; #[component(AboutPage)] -pub fn about_page() -> Template { +pub fn about_page() -> SycamoreTemplate { template! { p { "About." } } } -pub fn get_page() -> Page<(), G> { - Page::new("about") +pub fn get_page() -> Template<(), G> { + Template::new("about") .template(Box::new(|_| template! { AboutPage() } diff --git a/examples/showcase/app/src/pages/index.rs b/examples/showcase/app/src/pages/index.rs index 80c242a9d4..dea8fd8f13 100644 --- a/examples/showcase/app/src/pages/index.rs +++ b/examples/showcase/app/src/pages/index.rs @@ -1,6 +1,6 @@ -use sycamore::prelude::*; use serde::{Serialize, Deserialize}; -use perseus::page::Page; +use sycamore::prelude::{template, component, GenericNode, Template as SycamoreTemplate}; +use perseus::template::Template; #[derive(Serialize, Deserialize, Debug)] pub struct IndexPageProps { @@ -8,15 +8,15 @@ pub struct IndexPageProps { } #[component(IndexPage)] -pub fn index_page(props: IndexPageProps) -> Template { +pub fn index_page(props: IndexPageProps) -> SycamoreTemplate { template! { p {(props.greeting)} a(href = "/about") { "About!" } } } -pub fn get_page() -> Page { - Page::new("index") +pub fn get_page() -> Template { + Template::new("index") .build_state_fn(Box::new(get_static_props)) .template(Box::new(|props: Option| template! { IndexPage(props.unwrap()) diff --git a/examples/showcase/app/src/pages/post.rs b/examples/showcase/app/src/pages/post.rs index 5b22710b06..342dc61bed 100644 --- a/examples/showcase/app/src/pages/post.rs +++ b/examples/showcase/app/src/pages/post.rs @@ -1,6 +1,6 @@ -use sycamore::prelude::*; use serde::{Serialize, Deserialize}; -use perseus::page::Page; +use sycamore::prelude::{template, component, GenericNode, Template as SycamoreTemplate}; +use perseus::template::Template; #[derive(Serialize, Deserialize)] pub struct PostPageProps { @@ -9,7 +9,7 @@ pub struct PostPageProps { } #[component(PostPage)] -pub fn post_page(props: PostPageProps) -> Template { +pub fn post_page(props: PostPageProps) -> SycamoreTemplate { let title = props.title; let content = props.content; template! { @@ -22,8 +22,8 @@ pub fn post_page(props: PostPageProps) -> Template { } } -pub fn get_page() -> Page { - Page::new("post") +pub fn get_page() -> Template { + Template::new("post") .build_paths_fn(Box::new(get_static_paths)) .build_state_fn(Box::new(get_static_props)) .incremental_path_rendering(true) diff --git a/src/build.rs b/src/build.rs index 2f0c5620d0..9dd00c89bc 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,8 +1,8 @@ -// This binary builds all the pages with SSG +// This binary builds all the templates with SSG use serde::{Serialize, de::DeserializeOwned}; use crate::{ - page::Page, + template::Template, config_manager::ConfigManager, render_cfg::RenderOpt }; @@ -10,27 +10,27 @@ use crate::errors::*; use std::any::Any; use sycamore::prelude::SsrNode; -/// Builds a page, writing static data as appropriate. This should be used as part of a larger build process. -pub fn build_page(page: Page, config_manager: &impl ConfigManager) -> Result> { +/// Builds a template, writing static data as appropriate. This should be used as part of a larger build process. +pub fn build_template(template: Template, config_manager: &impl ConfigManager) -> Result> { let mut render_opts: Vec = Vec::new(); - let page_path = page.get_path(); + let template_path = template.get_path(); // Handle the boolean properties - if page.revalidates() { + if template.revalidates() { render_opts.push(RenderOpt::Revalidated); } - if page.uses_incremental() { + if template.uses_incremental() { render_opts.push(RenderOpt::Incremental); } // Handle static path generation // Because we iterate over the paths, we need a base path if we're not generating custom ones (that'll be overriden if needed) - let paths = match page.uses_build_paths() { + let paths = match template.uses_build_paths() { true => { render_opts.push(RenderOpt::StaticPaths); - page.get_build_paths()? + template.get_build_paths()? }, - false => vec![page_path.clone()] + false => vec![template_path.clone()] }; // Iterate through the paths to generate initial states if needed @@ -38,26 +38,26 @@ pub fn build_page(page: Page urlencoding::encode(&format!("{}/{}", &page_path, path)).to_string(), + true => urlencoding::encode(&format!("{}/{}", &template_path, path)).to_string(), // We don't want to concatenate the name twice if we don't have to - false => page_path.clone() + false => template_path.clone() }; // Handle static initial state generation // We'll only write a static state if one is explicitly generated - if page.uses_build_state() { + if template.uses_build_state() { render_opts.push(RenderOpt::StaticProps); // We pass in the latter part of the path, without the base specifier (because that would be the same for everything in the template) - let initial_state = page.get_build_state(path.to_string())?; + let initial_state = template.get_build_state(path.to_string())?; let initial_state_str = serde_json::to_string(&initial_state).unwrap(); // Write that intial state to a static JSON file config_manager .write(&format!("./dist/static/{}.json", full_path), &initial_state_str) .unwrap(); - // Prerender the page using that state + // Prerender the template using that state let prerendered = sycamore::render_to_string( || - page.render_for_template(Some(initial_state)) + template.render_for_template(Some(initial_state)) ); // Write that prerendered HTML to a static file config_manager @@ -67,17 +67,17 @@ pub fn build_page(page: Page(page: Page { let mut render_conf: $crate::render_cfg::RenderCfg = ::std::collections::HashMap::new(); $( render_conf.insert( - $page.get_path(), - $crate::build::build_page($page, $config_manager) + $template.get_path(), + $crate::build::build_template($template, $config_manager) .unwrap() ); )+ diff --git a/src/errors.rs b/src/errors.rs index fe37600eb5..8cfbb63bcf 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -13,9 +13,9 @@ error_chain! { display("the following error occurred while interfacing with javascript: {:?}", err) } - PageFeatureNotEnabled(name: String, feature: String) { - description("a page feature required by a function called was not present") - display("the page '{}' is missing the feature '{}'", name, feature) + TemplateFeatureNotEnabled(name: String, feature: String) { + description("a template feature required by a function called was not present") + display("the template '{}' is missing the feature '{}'", name, feature) } } links { diff --git a/src/lib.rs b/src/lib.rs index dd29b112a3..49e71b36f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,5 @@ pub mod shell; pub mod serve; pub mod render_cfg; pub mod config_manager; -pub mod page; +pub mod template; pub mod build; \ No newline at end of file diff --git a/src/serve.rs b/src/serve.rs index ae4535d4a1..ca3c62089e 100644 --- a/src/serve.rs +++ b/src/serve.rs @@ -28,6 +28,7 @@ pub fn get_render_cfg() -> Result { pub fn get_page(raw_path: &str, render_cfg: &RenderCfg, config_manager: &impl ConfigManager) -> Result { // Remove `/` from the path by encoding it as a URL (that's what we store) let path = urlencoding::encode(raw_path).to_string(); + // TODO Match the path to one of the templates // TODO support SSR // Get the static HTML diff --git a/src/shell.rs b/src/shell.rs index 709fdebcab..bb7e528ee8 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -6,7 +6,7 @@ use sycamore::prelude::*; use serde::{Serialize, de::DeserializeOwned}; use crate::errors::*; use crate::serve::PageData; -use crate::page::TemplateFn; +use crate::template::TemplateFn; pub async fn fetch(url: String) -> Result> { let js_err_handler = |err: JsValue| ErrorKind::JsErr(format!("{:?}", err)); diff --git a/src/page.rs b/src/template.rs similarity index 61% rename from src/page.rs rename to src/template.rs index 369c5aed56..c2073095ad 100644 --- a/src/page.rs +++ b/src/template.rs @@ -1,51 +1,51 @@ -// This file contains logic to define how pages are rendered +// This file contains logic to define how templates are rendered use crate::errors::*; use serde::{Serialize, de::DeserializeOwned}; -use sycamore::prelude::{Template, GenericNode}; +use sycamore::prelude::{Template as SycamoreTemplate, GenericNode}; // A series of closure types that should not be typed out more than once -pub type TemplateFn = Box) -> Template>; +pub type TemplateFn = Box) -> SycamoreTemplate>; pub type GetBuildPathsFn = Box Vec>; pub type GetBuildStateFn = Box Props>; pub type GetRequestStateFn = Box Props>; pub type ShouldRevalidateFn = Box bool>; -/// This allows the specification of all the page templates in an app and how to render them. If no rendering logic is provided at all, -/// the page will be prerendered at build-time with no state. All closures are stored on the heap to avoid hellish lifetime specification. +/// This allows the specification of all the template templates in an app and how to render them. If no rendering logic is provided at all, +/// the template will be prerendered at build-time with no state. All closures are stored on the heap to avoid hellish lifetime specification. // #[derive(Clone)] -pub struct Page +pub struct Template { /// The path to the root of the template. Any build paths will be inserted under this. path: String, - /// A function that will render your page. This will be provided the rendered properties, and will be used whenever your page needs - /// to be prerendered in some way. This should be very similar to the function that hydrates your page on the client side. + /// A function that will render your template. This will be provided the rendered properties, and will be used whenever your template needs + /// to be prerendered in some way. This should be very similar to the function that hydrates your template on the client side. /// This will be executed inside `sycamore::render_to_string`, and should return a `Template`. This takes an `Option` - /// because otherwise efficient typing is almost impossible for pages without any properties (solutions welcome in PRs!). + /// because otherwise efficient typing is almost impossible for templates without any properties (solutions welcome in PRs!). template: TemplateFn, /// A function that gets the paths to render for at built-time. This is equivalent to `get_static_paths` in NextJS. If /// `incremental_path_rendering` is `true`, more paths can be rendered at request time on top of these. get_build_paths: Option, /// Defiens whether or not any new paths that match this template will be prerendered and cached in production. This allows you to - /// have potentially billions of pages and retain a super-fast build process. The first user will have an ever-so-slightly slower + /// have potentially billions of templates and retain a super-fast build process. The first user will have an ever-so-slightly slower /// experience, and everyone else gets the beneftis afterwards. incremental_path_rendering: bool, - /// A function that gets the initial state to use to prerender the page at build time. This will be passed the path of the page, and + /// A function that gets the initial state to use to prerender the template at build time. This will be passed the path of the template, and /// will be run for any sub-paths. This is equivalent to `get_static_props` in NextJS. get_build_state: Option>, /// A function that will run on every request to generate a state for that request. This allows server-side-rendering. This is equivalent /// to `get_server_side_props` in NextJS. This can be used with `get_build_state`, though custom amalgamation logic must be provided. // TODO add request data to be passed in here get_request_state: Option>, - /// A function to be run on every request to check if a page prerendered at build-time should be prerendered again. This is equivalent + /// A function to be run on every request to check if a template prerendered at build-time should be prerendered again. This is equivalent /// to incremental static rendering (ISR) in NextJS. If used with `revalidate_after`, this function will only be run after that time /// period. This function will not be parsed anything specific to the request that invoked it. should_revalidate: Option, - /// A length of time after which to prerender the page again. This is equivalent to ISR in NextJS. + /// A length of time after which to prerender the template again. This is equivalent to ISR in NextJS. revalidate_after: Option, } -impl Page { - /// Creates a new page definition. +impl Template { + /// Creates a new template definition. pub fn new(path: impl Into + std::fmt::Display) -> Self { Self { path: path.to_string(), @@ -61,58 +61,58 @@ impl Page { } // Render executors - /// Executes the user-given function that renders the page on the server-side (build or request time). - pub fn render_for_template(&self, props: Option) -> Template { + /// Executes the user-given function that renders the template on the server-side (build or request time). + pub fn render_for_template(&self, props: Option) -> SycamoreTemplate { (self.template)(props) } - /// Gets the list of pages that should be prerendered for at build-time. + /// Gets the list of templates that should be prerendered for at build-time. pub fn get_build_paths(&self) -> Result> { if let Some(get_build_paths) = &self.get_build_paths { // TODO support error handling for render functions Ok(get_build_paths()) } else { - bail!(ErrorKind::PageFeatureNotEnabled(self.path.clone(), "build_paths".to_string())) + bail!(ErrorKind::TemplateFeatureNotEnabled(self.path.clone(), "build_paths".to_string())) } } - /// Gets the initial state for a page. This needs to be passed the full path of the page, which may be one of those generated by + /// Gets the initial state for a template. This needs to be passed the full path of the template, which may be one of those generated by /// `.get_build_paths()`. pub fn get_build_state(&self, path: String) -> Result { if let Some(get_build_state) = &self.get_build_state { // TODO support error handling for render functions Ok(get_build_state(path)) } else { - bail!(ErrorKind::PageFeatureNotEnabled(self.path.clone(), "build_state".to_string())) + bail!(ErrorKind::TemplateFeatureNotEnabled(self.path.clone(), "build_state".to_string())) } } // Value getters - /// Gets the path of the page. + /// Gets the path of the template. pub fn get_path(&self) -> String { self.path.clone() } // Render characteristic checkers - /// Checks if this page can revalidate existing prerendered pages. + /// Checks if this template can revalidate existing prerendered templates. pub fn revalidates(&self) -> bool { self.should_revalidate.is_some() || self.revalidate_after.is_some() } - /// Checks if this page can render more pages beyond those paths it explicitly defines. + /// Checks if this template can render more templates beyond those paths it explicitly defines. pub fn uses_incremental(&self) -> bool { self.incremental_path_rendering } - /// Checks if this page is a template to generate paths beneath it. + /// Checks if this template is a template to generate paths beneath it. pub fn uses_build_paths(&self) -> bool { self.get_build_paths.is_some() } - /// Checks if this page needs to do anything on requests for it. + /// Checks if this template needs to do anything on requests for it. pub fn uses_request_state(&self) -> bool { self.get_request_state.is_some() } - /// Checks if this page needs to do anything at build time. + /// Checks if this template needs to do anything at build time. pub fn uses_build_state(&self) -> bool { self.get_build_state.is_some() } - /// Checks if this page defines no rendering logic whatsoever. Such pages will be rendered using SSG. + /// Checks if this template defines no rendering logic whatsoever. Such templates will be rendered using SSG. pub fn is_basic(&self) -> bool { !self.uses_build_paths() && !self.uses_build_state() && @@ -122,31 +122,31 @@ impl Page { } // Builder setters - pub fn template(mut self, val: TemplateFn) -> Page { + pub fn template(mut self, val: TemplateFn) -> Template { self.template = val; self } - pub fn build_paths_fn(mut self, val: GetBuildPathsFn) -> Page { + pub fn build_paths_fn(mut self, val: GetBuildPathsFn) -> Template { self.get_build_paths = Some(val); self } - pub fn incremental_path_rendering(mut self, val: bool) -> Page { + pub fn incremental_path_rendering(mut self, val: bool) -> Template { self.incremental_path_rendering = val; self } - pub fn build_state_fn(mut self, val: GetBuildStateFn) -> Page { + pub fn build_state_fn(mut self, val: GetBuildStateFn) -> Template { self.get_build_state = Some(val); self } - pub fn request_state_fn(mut self, val: GetRequestStateFn) -> Page { + pub fn request_state_fn(mut self, val: GetRequestStateFn) -> Template { self.get_request_state = Some(val); self } - pub fn should_revalidate(mut self, val: ShouldRevalidateFn) -> Page { + pub fn should_revalidate(mut self, val: ShouldRevalidateFn) -> Template { self.should_revalidate = Some(val); self } - pub fn revalidate_after(mut self, val: String) -> Page { + pub fn revalidate_after(mut self, val: String) -> Template { self.revalidate_after = Some(val); self }