From 6ec48529e2371dc464d993f3743091b9cccb0a96 Mon Sep 17 00:00:00 2001 From: arctic_hen7 Date: Thu, 7 Jul 2022 18:47:53 +1000 Subject: [PATCH] docs: substantially improved code-level docs Much more to do, but it's coming along! --- packages/perseus/src/error_pages.rs | 33 +++++++-- packages/perseus/src/errors.rs | 5 +- packages/perseus/src/init.rs | 91 +++++++++++++---------- packages/perseus/src/stores/immutable.rs | 19 +++-- packages/perseus/src/stores/mutable.rs | 24 ++++-- packages/perseus/src/template/core.rs | 93 ++++++++++++++++-------- 6 files changed, 171 insertions(+), 94 deletions(-) diff --git a/packages/perseus/src/error_pages.rs b/packages/perseus/src/error_pages.rs index 16b17ab844..32b0b5d1f3 100644 --- a/packages/perseus/src/error_pages.rs +++ b/packages/perseus/src/error_pages.rs @@ -21,7 +21,26 @@ use web_sys::Element; pub type ErrorPageTemplate = Box>) -> View + Send + Sync>; -/// A type alias for the `HashMap` the user should provide for error pages. +/// A representation of the views configured in an app for responding to errors. +/// +/// On the web, errors occur frequently beyond app logic, usually in communication +/// with servers, which will return [HTTP status codes](https://httpstatuses.com/) that indicate +/// a success or failure. If a non-success error code is received, then Perseus will +/// automatically render the appropriate error page, based on that status code. +/// If no page has been explicitly constructed for that status code, then the fallback +/// page will be used. +/// +/// Each error page is a closure returning a [`View`] that takes four parameters: +/// a reactive scope, the URL the user was on when the error occurred (which they'll still +/// be on, no route change occurs when rendering an error page), the status code itself, +/// a `String` of the actual error message, and a [`Translator`] (which may not be available +/// if the error occurred before translations data could be fetched and processed, in which +/// case you should try to display language-agnostic information). +/// +/// In development, you can get away with not defining any error pages for your app, as +/// Perseus has a simple inbuilt default, though, when you try to go to production (e.g. with `perseus deploy`), +/// you'll receive an error message in building. In other words, you must define your own error +/// pages for release mode. pub struct ErrorPages { status_pages: HashMap>, fallback: ErrorPageTemplate, @@ -32,7 +51,9 @@ impl std::fmt::Debug for ErrorPages { } } impl ErrorPages { - /// Creates a new definition of error pages with just a fallback. + /// Creates a new definition of error pages with just a fallback page, which will + /// be used when an error occurs whose status code has not been explicitly handled by + /// some other error page. pub fn new( fallback: impl Fn(Scope, String, u16, String, Option>) -> View + Send @@ -45,8 +66,10 @@ impl ErrorPages { } } /// Adds a new page for the given status code. If a page was already defined - /// for the given code, it will be updated by the mechanics of - /// the internal `HashMap`. + /// for the given code, it will be updated by replacement, through the mechanics of + /// the internal `HashMap`. While there is no requirement for this to be a + /// valid HTTP status code, there would be no point in defining a handler + /// for a status code not on [this list](https://httpstatuses.com) pub fn add_page( &mut self, status: u16, @@ -60,7 +83,7 @@ impl ErrorPages { /// Adds a new page for the given status code. If a page was already defined /// for the given code, it will be updated by the mechanics of /// the internal `HashMap`. This differs from `.add_page()` in that it takes - /// an `Rc`, which is useful for plugins. + /// an `Rc`, which can be useful for plugins. pub fn add_page_rc(&mut self, status: u16, page: ErrorPageTemplate) { self.status_pages.insert(status, page); } diff --git a/packages/perseus/src/errors.rs b/packages/perseus/src/errors.rs index 5fa2759f9f..4ab7455042 100644 --- a/packages/perseus/src/errors.rs +++ b/packages/perseus/src/errors.rs @@ -255,9 +255,8 @@ impl From for GenericErrorWithC } } -/// Creates a new `GenericErrorWithCause` efficiently. This allows you to -/// explicitly return errors from anything that returns -/// `RenderFnResultWithCause`, which includes both an error and a statement of +/// Creates a new [`GenericErrorWithCause` (the error type behind [`RenderFnResultWithCause`]) efficiently. This allows you to +/// explicitly return errors from any state-generation functions, including both an error and a statement of /// whether the server or the client is responsible. With this macro, you can /// use any of the following syntaxes (substituting `"error!"` for any error /// that can be converted with `.into()` into a `Box`): diff --git a/packages/perseus/src/init.rs b/packages/perseus/src/init.rs index 29e2d1c096..533d88d5d1 100644 --- a/packages/perseus/src/init.rs +++ b/packages/perseus/src/init.rs @@ -93,12 +93,9 @@ where } } -/// The options for constructing a Perseus app. These encompass all the -/// information Perseus needs to know to create your app. Every Perseus app -/// using the engine must export one of these. -/// -/// Note that this is an interim storage point, it's not depended on by any part -/// of the core logic, and therefore custom engines can entirely ignore this. +/// The options for constructing a Perseus app. This `struct` will tie +/// together all your code, declaring to Perseus where your templates, +/// error pages, static content, etc. are. #[derive(Debug)] pub struct PerseusAppBase { /// The HTML ID of the root `
` element into which Perseus will be @@ -157,8 +154,8 @@ pub struct PerseusAppBase { // because things are completely generic there impl PerseusAppBase { /// Creates a new instance of a Perseus app using the default - /// filesystem-based mutable store. For most apps, this will be sufficient. - /// Note that this initializes the translations manager as a dummy, and + /// filesystem-based mutable store (see [`FsMutableStore`]). For most apps, this will be sufficient. + /// Note that this initializes the translations manager as a dummy (see [`FsTranslationsManager`]), and /// adds no templates or error pages. /// /// In development, you can get away with defining no error pages, but @@ -174,8 +171,8 @@ impl PerseusAppBase { Self::new_with_mutable_store(FsMutableStore::new("./dist/mutable".to_string())) } /// Creates a new instance of a Perseus app using the default - /// filesystem-based mutable store. For most apps, this will be sufficient. - /// Note that this initializes the translations manager as a dummy, and + /// filesystem-based mutable store (see [`FsMutableStore`]). For most apps, this will be sufficient. + /// Note that this initializes the translations manager as a dummy (see [`FsTranslationsManager`]), and /// adds no templates or error pages. /// /// In development, you can get away with defining no error pages, but @@ -195,8 +192,8 @@ impl PerseusAppBase { // automatically for them impl PerseusAppBase { /// The same as `.locales_and_translations_manager()`, but this accepts a - /// literal `Locales` `struct`, which means this can be used when you're - /// using `FsTranslationsManager` but when you don't know if your app is + /// literal [`Locales`] `struct`, which means this can be used when you're + /// using [`FsTranslationsManager`] but when you don't know if your app is /// using i18n or not (almost always middleware). pub fn locales_lit_and_translations_manager(mut self, locales: Locales) -> Self { #[cfg(not(target_arch = "wasm32"))] @@ -233,7 +230,7 @@ impl PerseusAppBase { self } /// Sets the internationalization information for an app using the default - /// translations manager (`FsTranslationsManager`). This handles locale + /// translations manager ([`FsTranslationsManager`]). This handles locale /// caching and the like automatically for you, though you could /// alternatively use `.locales()` and `.translations_manager()` /// independently to customize various behaviors. This takes the same @@ -260,7 +257,8 @@ impl PerseusAppBase { // manager impl PerseusAppBase { /// Creates a new instance of a Perseus app, with the default options and a - /// custom mutable store. + /// customizable [`MutableStore`], using the default dummy [`FsTranslationsManager`] + /// by default (though this can be changed). #[allow(unused_variables)] pub fn new_with_mutable_store(mutable_store: M) -> Self { Self { @@ -302,6 +300,7 @@ impl PerseusAppBase { /// Internal function for Wasm initialization. This should never be called /// by the user! #[cfg(target_arch = "wasm32")] + #[doc(hidden)] fn new_wasm() -> Self { Self { root: "root".to_string(), @@ -328,12 +327,18 @@ impl PerseusAppBase { // Setters (these all consume `self`) /// Sets the HTML ID of the `
` element at which to insert Perseus. + /// In your index view, this should use [`PerseusRoot`]. + /// + /// *Note:* if you're using string HTML, the `
` with this ID MUST look + /// *exactly* like this: `
`, spacing and + /// all! pub fn root(mut self, val: &str) -> Self { self.root = val.to_string(); self } /// Sets the location of the directory storing static assets to be hosted - /// under the URL `/.perseus/static/`. + /// under the URL `/.perseus/static/`. By default, this is `static/` at + /// the root of your project. #[allow(unused_variables)] #[allow(unused_mut)] pub fn static_dir(mut self, val: &str) -> Self { @@ -345,22 +350,27 @@ impl PerseusAppBase { } /// Sets all the app's templates. This takes a vector of boxed functions /// that return templates. + /// + /// Usually, it's preferred to run `.template()` once for each template, + /// rather than manually constructing this more inconvenient type. pub fn templates(mut self, val: Vec Template>>) -> Self { self.template_getters.0 = val; self } /// Adds a single new template to the app (convenience function). This takes - /// a function that returns a template. + /// a *function that returns a template* (for internal reasons). + /// + /// See [`Template`] for further details. pub fn template(mut self, val: impl Fn() -> Template + 'static) -> Self { self.template_getters.0.push(Box::new(val)); self } - /// Sets the app's error pages. + /// Sets the app's error pages. See [`ErrorPages`] for further details. pub fn error_pages(mut self, val: impl Fn() -> ErrorPages + 'static) -> Self { self.error_pages = ErrorPagesGetter(Box::new(val)); self } - /// Sets the app's global state creator. + /// Sets the app's [`GlobalStateCreator`]. #[allow(unused_variables)] #[allow(unused_mut)] pub fn global_state_creator(mut self, val: GlobalStateCreator) -> Self { @@ -376,7 +386,7 @@ impl PerseusAppBase { /// supported. /// /// Note that this does not update the translations manager, which must be - /// done separately (if you're using `FsTranslationsManager`, the default, + /// done separately (if you're using [`FsTranslationsManager`], the default, /// you can use `.locales_and_translations_manager()` to set both at /// once). /// @@ -393,7 +403,7 @@ impl PerseusAppBase { }; self } - /// Sets the locales information directly based on an instance of `Locales`. + /// Sets the locales information directly based on an instance of [`Locales`]. /// Usually, end users will use `.locales()` instead for a friendlier /// interface. pub fn locales_lit(mut self, val: Locales) -> Self { @@ -401,15 +411,15 @@ impl PerseusAppBase { self } /// Sets the translations manager. If you're using the default translations - /// manager (`FsTranslationsManager`), you can use + /// manager ([`FsTranslationsManager`]), you can use /// `.locales_and_translations_manager()` to set this automatically /// based on the locales information. This takes a `Future`, /// where `T` is your translations manager's type. /// /// The reason that this takes a `Future` is to avoid the use of `.await` in /// your app definition code, which must be synchronous due to constraints - /// of Perseus' client-side systems. When your code is run on the - /// server, the `Future` will be `.await`ed on, but on Wasm, it will be + /// of Perseus' browser-side systems. When your code is run on the + /// server, the `Future` will be `.await`ed on, but in Wasm, it will be /// discarded and ignored, since the translations manager isn't needed in /// Wasm. /// @@ -444,7 +454,8 @@ impl PerseusAppBase { } /// Sets all the app's static aliases. This takes a map of URLs (e.g. /// `/file`) to resource paths, relative to the project directory (e.g. - /// `style.css`). + /// `style.css`). Generally, calling `.static_alias()` many times is + /// preferred. #[allow(unused_variables)] #[allow(unused_mut)] pub fn static_aliases(mut self, val: HashMap) -> Self { @@ -454,8 +465,7 @@ impl PerseusAppBase { } self } - /// Adds a single static alias (convenience function). This takes a URL path - /// (e.g. `/file`) followed by a path to a resource (which must be within + /// Adds a single static alias. This takes a URL path (e.g. `/file`) followed by a path to a resource (which must be within /// the project directory, e.g. `style.css`). #[allow(unused_variables)] #[allow(unused_mut)] @@ -466,12 +476,13 @@ impl PerseusAppBase { .insert(url.to_string(), resource.to_string()); self } - /// Sets the plugins that the app will use. + /// Sets the plugins that the app will use. See [`Plugins`] for + /// further details. pub fn plugins(mut self, val: Plugins) -> Self { self.plugins = Rc::new(val); self } - /// Sets the mutable store for the app to use, which you would change for + /// Sets the [`MutableStore`] for the app to use, which you would change for /// some production server environments if you wanted to store build /// artifacts that can change at runtime in a place other than on the /// filesystem (created for serverless functions specifically). @@ -484,7 +495,7 @@ impl PerseusAppBase { } self } - /// Sets the immutable store for the app to use. You should almost never + /// Sets the [`ImmutableStore`] for the app to use. You should almost never /// need to change this unless you're not working with the CLI. #[allow(unused_variables)] #[allow(unused_mut)] @@ -550,7 +561,7 @@ impl PerseusAppBase { /// this into `::get_html_shell()` to do that). /// /// Note that this automatically adds `` to the start of the - /// HTMl shell produced, which can only be overriden with a control plugin + /// HTML shell produced, which can only be overriden with a control plugin /// (though you should really never do this in Perseus, which targets /// HTML on the web). #[cfg(not(target_arch = "wasm32"))] @@ -715,7 +726,7 @@ impl PerseusAppBase { map } - /// Gets the error pages used in the app. This returns an `Rc`. + /// Gets the [`ErrorPages`] used in the app. This returns an `Rc`. pub fn get_error_pages(&self) -> ErrorPages { let mut error_pages = (self.error_pages.0)(); let extra_error_pages = self @@ -732,7 +743,7 @@ impl PerseusAppBase { error_pages } - /// Gets the global state creator. This can't be directly modified by + /// Gets the [`GlobalStateCreator`]. This can't be directly modified by /// plugins because of reactive type complexities. #[cfg(not(target_arch = "wasm32"))] pub fn get_global_state_creator(&self) -> Arc { @@ -748,7 +759,7 @@ impl PerseusAppBase { .run(locales.clone(), self.plugins.get_plugin_data()) .unwrap_or(locales) } - /// Gets the server-side translations manager. Like the mutable store, this + /// Gets the server-side [`TranslationsManager`]. Like the mutable store, this /// can't be modified by plugins due to trait complexities. /// /// This involves evaluating the future stored for the translations manager, @@ -760,7 +771,7 @@ impl PerseusAppBase { Tm::Full(tm) => tm.await, } } - /// Gets the immutable store. + /// Gets the [`ImmutableStore`]. #[cfg(not(target_arch = "wasm32"))] pub fn get_immutable_store(&self) -> ImmutableStore { let immutable_store = self.immutable_store.clone(); @@ -771,7 +782,7 @@ impl PerseusAppBase { .run(immutable_store.clone(), self.plugins.get_plugin_data()) .unwrap_or(immutable_store) } - /// Gets the mutable store. This can't be modified by plugins due to trait + /// Gets the [`MutableStore`]. This can't be modified by plugins due to trait /// complexities, so plugins should instead expose a function that the user /// can use to manually set it. #[cfg(not(target_arch = "wasm32"))] @@ -838,7 +849,7 @@ impl PerseusAppBase { } /// The component that represents the entrypoint at which Perseus will inject -/// itself. You can use this with the `.index_view()` method of `PerseusApp` to +/// itself. You can use this with the `.index_view()` method of [`PerseusAppBase`] to /// avoid having to create the entrypoint `
` manually. #[component] #[allow(non_snake_case)] @@ -852,13 +863,13 @@ use crate::i18n::FsTranslationsManager; use crate::stores::FsMutableStore; /// An alias for the usual kind of Perseus app, which uses the filesystem-based -/// mutable store and translations manager. +/// mutable store and translations manager. See [`PerseusAppBase`] for details. pub type PerseusApp = PerseusAppBase; -/// An alias for a Perseus app that uses a custom mutable store type. +/// An alias for a Perseus app that uses a custom mutable store type. See [`PerseusAppBase`] for details. pub type PerseusAppWithMutableStore = PerseusAppBase; -/// An alias for a Perseus app that uses a custom translations manager type. +/// An alias for a Perseus app that uses a custom translations manager type. See [`PerseusAppBase`] for details. pub type PerseusAppWithTranslationsManager = PerseusAppBase; /// An alias for a fully customizable Perseus app that can accept a custom /// mutable store and a custom translations manager. Alternatively, you could -/// just use `PerseusAppBase` directly. +/// just use [`PerseusAppBase`] directly. pub type PerseusAppWithMutableStoreAndTranslationsManager = PerseusAppBase; diff --git a/packages/perseus/src/stores/immutable.rs b/packages/perseus/src/stores/immutable.rs index 21fb3acc88..16cb095a20 100644 --- a/packages/perseus/src/stores/immutable.rs +++ b/packages/perseus/src/stores/immutable.rs @@ -6,14 +6,16 @@ use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, }; -/// An immutable storage system. This wraps filesystem calls in a sensible -/// asynchronous API, allowing abstraction of the base path to a distribution -/// directory or the like. Perseus uses this to store assts created at build -/// time that won't change, which is anything not involved in the *revalidation* -/// or *incremental generation* strategies. +/// An immutable storage system used by Perseus' engine to store build artifacts +/// and the like, which will then be used by the server or the export process. By default, +/// this is set to a path inside the `dist/` folder at the root of your project, +/// which you should only change if you have special requirements, as the CLI +/// expects the default paths to be used, with no option for customization yet. /// -/// Note: the `.write()` methods on this implementation will create any missing -/// parent directories automatically. +/// Note that this is only used for immutable data, which can be read-only in production, +/// meaning there are no consequences of using this on a read-only production filesystem +/// (e.g. in a serverless function). Data that do need to change use a [`MutableStore`](super::MutableStore) +/// instead. #[derive(Clone, Debug)] pub struct ImmutableStore { #[cfg(not(target_arch = "wasm32"))] @@ -71,7 +73,8 @@ impl ImmutableStore { } } /// Writes the given asset to the filesystem asynchronously. This must only - /// be used at build-time, and must not be changed afterward. + /// be used at build-time, and must not be changed afterward. Note that this + /// will automatically create any missing parent directories. #[cfg(not(target_arch = "wasm32"))] pub async fn write(&self, name: &str, content: &str) -> Result<(), StoreError> { let asset_path = format!("{}/{}", self.root_path, name); diff --git a/packages/perseus/src/stores/mutable.rs b/packages/perseus/src/stores/mutable.rs index ca3b1bdadd..5736eb1733 100644 --- a/packages/perseus/src/stores/mutable.rs +++ b/packages/perseus/src/stores/mutable.rs @@ -5,10 +5,20 @@ use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, }; -/// A trait for mutable stores. This is abstracted away so that users can -/// implement a non-filesystem mutable store, which is useful for read-only -/// filesystem environments, as on many modern hosting providers. See the book -/// for further details on this subject. +/// A trait for implementations of stores that the Perseus engine can use for +/// mutable data, which may need to be altered while the server is running. In exported +/// apps, this is irrelevant, since there is no server process to speak of. +/// This store is used in particular by the revalidation and incremental generation +/// strategies, which will both update build artifacts for future caching. +/// By default, [`FsMutableStore`] is used for simplicity and low latency, though +/// this is only viable in deployments with writable filesystems. Notably, this +/// precludes usage in serverless functions. +/// +/// However, this is written deliberately as a trait with exposed, isolated error +/// types (see [`StoreError`]), so that users can write their own implementations +/// of this. For instance, you could manage mutable artifacts in a database, +/// though this should be as low-latency as possible, since reads and writes +/// are required at extremely short-notice as new user requests arrive. #[async_trait::async_trait] pub trait MutableStore: std::fmt::Debug + Clone + Send + Sync { /// Reads data from the named asset. @@ -18,11 +28,11 @@ pub trait MutableStore: std::fmt::Debug + Clone + Send + Sync { async fn write(&self, name: &str, content: &str) -> Result<(), StoreError>; } -/// The default mutable store, which simply uses the filesystem. This is +/// The default [`MutableStore`], which simply uses the filesystem. This is /// suitable for development and production environments with /// writable filesystems (in which it's advised), but this is of course not /// usable on production read-only filesystems, and another implementation of -/// `MutableStore` should be preferred. +/// [`MutableStore`] should be preferred. /// /// Note: the `.write()` methods on this implementation will create any missing /// parent directories automatically. @@ -35,7 +45,7 @@ pub struct FsMutableStore { impl FsMutableStore { /// Creates a new filesystem configuration manager. You should provide a /// path like `dist/mutable` here. Make sure that this is not the same - /// path as the immutable store, as this will cause potentially problematic + /// path as the [`ImmutableStore`](super::ImmutableStore), as this will cause potentially problematic /// overlap between the two systems. #[cfg(not(target_arch = "wasm32"))] pub fn new(root_path: String) -> Self { diff --git a/packages/perseus/src/template/core.rs b/packages/perseus/src/template/core.rs index d0f2926776..d9c1eb1658 100644 --- a/packages/perseus/src/template/core.rs +++ b/packages/perseus/src/template/core.rs @@ -31,7 +31,7 @@ use sycamore::utils::hydrate::with_no_hydration_context; /// type. pub type RenderFnResult = std::result::Result>; /// A generic error type that can be adapted for any errors the user may want to -/// return from a render function, as with `RenderFnResult`. However, this +/// return from a render function, as with [`RenderFnResult`]. However, this /// also includes a mandatory statement of causation for any errors, which /// assigns blame for them to either the client or the server. In cases where /// this is ambiguous, this allows returning accurate HTTP status codes. @@ -39,8 +39,8 @@ pub type RenderFnResult = std::result::Result = std::result::Result; // A series of asynchronous closure traits that prevent the user from having to @@ -99,13 +99,20 @@ pub type ShouldRevalidateFn = Box; pub type AmalgamateStatesFn = Box RenderFnResultWithCause> + Send + Sync>; -/// 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. All properties for -/// templates are passed around as strings to avoid type maps and other horrible -/// things, this only adds one extra deserialization call at build time. This -/// only actually owns a two `String`s and a `bool`. +/// A single template in an app. Each template is comprised of a Sycamore view, +/// a state type, and some functions involved with generating that state. Pages +/// can then be generated from particular states. For instance, a single `docs` +/// template could have a state `struct` that stores a title and some content, +/// which could then render as many pages as desired. +/// +/// You can read more about the templates system [here](https://arctic-hen7.github.io/perseus/en-US/docs/next/core-principles). +/// +/// Note that all template states are passed around as `String`s to avoid +/// type maps and other inefficiencies, since they need to be transmitted over +/// the network anyway. As such, this `struct` is entirely state-agnostic, +/// since all the state-relevant functions merely return `String`s. The +/// various proc macros used to annotate such functions (e.g. `#[perseus::build_state]`) +/// perform serialization/deserialization automatically for convenience. pub struct Template { /// The path to the root of the template. Any build paths will be inserted /// under this. @@ -134,8 +141,7 @@ pub struct Template { /// create sensible cache control headers. #[cfg(not(target_arch = "wasm32"))] set_headers: SetHeadersFn, - /// A function that gets the paths to render for at built-time. This is - /// equivalent to `get_static_paths` in NextJS. If + /// A function that gets the paths to render for at built-time. If /// `incremental_generation` is `true`, more paths can be rendered at /// request time on top of these. #[cfg(not(target_arch = "wasm32"))] @@ -152,26 +158,21 @@ pub struct Template { incremental_generation: bool, /// 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. + /// will be run for any sub-paths. #[cfg(not(target_arch = "wasm32"))] 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 + /// request. This allows server-side-rendering. This can be used with /// `get_build_state`, though custom amalgamation logic must be provided. #[cfg(not(target_arch = "wasm32"))] get_request_state: Option, /// 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 revalidation after a time in NextJS, with the improvement of custom - /// logic. If used with `revalidate_after`, this function will + /// at build-time should be prerendered again. 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. #[cfg(not(target_arch = "wasm32"))] should_revalidate: Option, - /// A length of time after which to prerender the template again. This is - /// equivalent to revalidating in NextJS. This should specify a + /// A length of time after which to prerender the template again. This should specify a /// string interval to revalidate after. That will be converted into a /// datetime to wait for, which will be updated after every revalidation. /// Note that, if this is used with incremental generation, the counter will @@ -200,7 +201,8 @@ impl std::fmt::Debug for Template { } } impl Template { - /// Creates a new template definition. + /// Creates a new [`Template`]. By default, this has absolutely no + /// associated data. If rendered, it would result in a blank screen. pub fn new(path: impl Into + std::fmt::Display) -> Self { Self { path: path.to_string(), @@ -230,7 +232,7 @@ impl Template { // Render executors /// Executes the user-given function that renders the template on the - /// client-side ONLY. This takes in an extsing global state. + /// client-side ONLY. This takes in an existing global state. #[cfg(target_arch = "wasm32")] #[allow(clippy::too_many_arguments)] pub fn render_for_template_client<'a>( @@ -500,7 +502,10 @@ impl Template { // we can avoid bringing in the whole `http` module --- a very significant // saving!) The macros handle the creation of empty functions to make user's // lives easier - /// Sets the template rendering function to use. + /// Sets the template rendering function to use. This function might take in + /// some state (use the `#[perseus::template_rx]` macro for serialization + /// convenience) and/or some global state, and then it must return a + /// Sycamore [`View`]. pub fn template( mut self, val: impl Fn(Scope, PageProps) -> View + Send + Sync + 'static, @@ -509,7 +514,9 @@ impl Template { self } - /// Sets the document head rendering function to use. + /// Sets the document `` rendering function to use. The [`View`] produced + /// by this will only be rendered on the engine-side, and will *not* be + /// reactive (since it only contains metadata). #[cfg(not(target_arch = "wasm32"))] pub fn head( mut self, @@ -519,7 +526,9 @@ impl Template { self.head = Box::new(val); self } - /// Sets the document head rendering function to use. + /// Sets the document `` rendering function to use. The [`View`] produced + /// by this will only be rendered on the engine-side, and will *not* be + /// reactive (since it only contains metadata). #[cfg(target_arch = "wasm32")] pub fn head(self, _val: impl Fn() + 'static) -> Template { self @@ -617,20 +626,39 @@ impl Template { } /// Enables the *revalidation* strategy (time variant). This takes a time - /// string of a form like `1w` for one week. More details are available [in the book](https://arctic-hen7.github.io/perseus/strategies/revalidation.html#time-syntax). + /// string of a form like `1w` for one week. + /// + /// - s: second, + /// - m: minute, + /// - h: hour, + /// - d: day, + /// - w: week, + /// - M: month (30 days used here, 12M ≠ 1y!), + /// - y: year (365 days always, leap years ignored, if you want them add them as days) #[cfg(not(target_arch = "wasm32"))] pub fn revalidate_after(mut self, val: String) -> Template { self.revalidate_after = Some(val); self } /// Enables the *revalidation* strategy (time variant). This takes a time - /// string of a form like `1w` for one week. More details are available [in the book](https://arctic-hen7.github.io/perseus/strategies/revalidation.html#time-syntax). + /// string of a form like `1w` for one week. + /// + /// - s: second, + /// - m: minute, + /// - h: hour, + /// - d: day, + /// - w: week, + /// - M: month (30 days used here, 12M ≠ 1y!), + /// - y: year (365 days always, leap years ignored, if you want them add them as days) #[cfg(target_arch = "wasm32")] pub fn revalidate_after(self, _val: String) -> Template { self } - /// Enables state amalgamation with the given function. + /// Enables state amalgamation with the given function. State amalgamation allows you to have one template + /// generate state at both build time and request time. The function you provide here is responsible for + /// rationalizing the two into one single state to be sent to the client, and this will be run just after + /// the request state function completes. See [`States`] for further details. #[cfg(not(target_arch = "wasm32"))] pub fn amalgamate_states_fn( mut self, @@ -639,7 +667,10 @@ impl Template { self.amalgamate_states = Some(Box::new(val)); self } - /// Enables state amalgamation with the given function. + /// Enables state amalgamation with the given function. State amalgamation allows you to have one template + /// generate state at both build time and request time. The function you provide here is responsible for + /// rationalizing the two into one single state to be sent to the client, and this will be run just after + /// the request state function completes. See [`States`] for further details. #[cfg(target_arch = "wasm32")] pub fn amalgamate_states_fn(self, _val: impl Fn() + 'static) -> Template { self @@ -656,7 +687,7 @@ pub type TemplateNodeType = sycamore::prelude::DomNode; pub type TemplateNodeType = sycamore::prelude::HydrateNode; /// Checks if we're on the server or the client. This must be run inside a -/// reactive scope (e.g. a `template!` or `create_effect`), because it uses +/// reactive scope (e.g. a `view!` or `create_effect`), because it uses /// Sycamore context. // TODO (0.4.0) Remove this altogether #[macro_export]