incremental_cfg.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #![allow(non_snake_case)]
  2. use crate::incremental::IncrementalRenderer;
  3. use crate::incremental::IncrementalRendererError;
  4. use std::{
  5. io::Write,
  6. num::NonZeroUsize,
  7. path::{Path, PathBuf},
  8. sync::Arc,
  9. time::Duration,
  10. };
  11. /// Something that can render a HTML page from a body.
  12. pub trait WrapBody {
  13. /// Render the HTML before the body
  14. fn render_before_body<R: Write>(&self, to: &mut R) -> Result<(), IncrementalRendererError>;
  15. /// Render the HTML after the body
  16. fn render_after_body<R: Write>(&self, to: &mut R) -> Result<(), IncrementalRendererError>;
  17. }
  18. /// The default page renderer
  19. pub struct DefaultRenderer {
  20. /// The HTML before the body.
  21. pub before_body: String,
  22. /// The HTML after the body.
  23. pub after_body: String,
  24. }
  25. impl Default for DefaultRenderer {
  26. fn default() -> Self {
  27. let title = dioxus_cli_config::CURRENT_CONFIG
  28. .as_ref()
  29. .map(|c| c.dioxus_config.application.name.clone())
  30. .unwrap_or("Dioxus Application".into());
  31. let before = format!(
  32. r#"<!DOCTYPE html>
  33. <html lang="en">
  34. <head>
  35. <meta charset="UTF-8">
  36. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  37. <title>{}</title>
  38. </head>
  39. <body>"#,
  40. title
  41. );
  42. let after = r#"</body>
  43. </html>"#;
  44. Self {
  45. before_body: before.to_string(),
  46. after_body: after.to_string(),
  47. }
  48. }
  49. }
  50. impl WrapBody for DefaultRenderer {
  51. fn render_before_body<R: Write>(&self, to: &mut R) -> Result<(), IncrementalRendererError> {
  52. to.write_all(self.before_body.as_bytes())?;
  53. Ok(())
  54. }
  55. fn render_after_body<R: Write>(&self, to: &mut R) -> Result<(), IncrementalRendererError> {
  56. to.write_all(self.after_body.as_bytes())?;
  57. Ok(())
  58. }
  59. }
  60. pub(crate) type PathMapFn = Arc<dyn Fn(&str) -> PathBuf + Send + Sync>;
  61. /// A configuration for the incremental renderer.
  62. #[derive(Clone)]
  63. pub struct IncrementalRendererConfig {
  64. static_dir: PathBuf,
  65. memory_cache_limit: usize,
  66. invalidate_after: Option<Duration>,
  67. map_path: Option<PathMapFn>,
  68. clear_cache: bool,
  69. pre_render: bool,
  70. }
  71. impl Default for IncrementalRendererConfig {
  72. fn default() -> Self {
  73. Self::new()
  74. }
  75. }
  76. impl IncrementalRendererConfig {
  77. /// Create a new incremental renderer configuration.
  78. pub fn new() -> Self {
  79. Self {
  80. static_dir: PathBuf::from("./static"),
  81. memory_cache_limit: 10000,
  82. invalidate_after: None,
  83. map_path: None,
  84. clear_cache: true,
  85. pre_render: false,
  86. }
  87. }
  88. /// Clear the cache on startup (default: true)
  89. pub fn clear_cache(mut self, clear_cache: bool) -> Self {
  90. self.clear_cache = clear_cache;
  91. self
  92. }
  93. /// Set a mapping from the route to the file path. This will override the default mapping configured with `static_dir`.
  94. /// The function should return the path to the folder to store the index.html file in.
  95. pub fn map_path<F: Fn(&str) -> PathBuf + Send + Sync + 'static>(mut self, map_path: F) -> Self {
  96. self.map_path = Some(Arc::new(map_path));
  97. self
  98. }
  99. /// Set the static directory.
  100. pub fn static_dir<P: AsRef<Path>>(mut self, static_dir: P) -> Self {
  101. self.static_dir = static_dir.as_ref().to_path_buf();
  102. self
  103. }
  104. /// Set the memory cache limit.
  105. pub const fn memory_cache_limit(mut self, memory_cache_limit: usize) -> Self {
  106. self.memory_cache_limit = memory_cache_limit;
  107. self
  108. }
  109. /// Set the invalidation time.
  110. pub fn invalidate_after(mut self, invalidate_after: Duration) -> Self {
  111. self.invalidate_after = Some(invalidate_after);
  112. self
  113. }
  114. /// Set whether to include hydration ids in the pre-rendered html.
  115. pub fn pre_render(mut self, pre_render: bool) -> Self {
  116. self.pre_render = pre_render;
  117. self
  118. }
  119. /// Build the incremental renderer.
  120. pub fn build(self) -> IncrementalRenderer {
  121. let static_dir = self.static_dir.clone();
  122. let mut ssr_renderer = crate::Renderer::new();
  123. if self.pre_render {
  124. ssr_renderer.pre_render = true;
  125. }
  126. let mut renderer = IncrementalRenderer {
  127. static_dir: self.static_dir.clone(),
  128. memory_cache: NonZeroUsize::new(self.memory_cache_limit)
  129. .map(|limit| lru::LruCache::with_hasher(limit, Default::default())),
  130. invalidate_after: self.invalidate_after,
  131. ssr_renderer,
  132. map_path: self.map_path.unwrap_or_else(move || {
  133. Arc::new(move |route: &str| {
  134. let (before_query, _) = route.split_once('?').unwrap_or((route, ""));
  135. let mut path = static_dir.clone();
  136. for segment in before_query.split('/') {
  137. path.push(segment);
  138. }
  139. path
  140. })
  141. }),
  142. };
  143. if self.clear_cache {
  144. renderer.invalidate_all();
  145. }
  146. renderer
  147. }
  148. }