Merge pull request #1948 from atty303/fullstack-wasm

feat(fullstack): support wasm target
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-02-19 08:52:05 -06:00
commit c0f2e8391c
11 files changed
+199 -158

No files matched your search

Generated
+1
View File
@@ -2775,6 +2775,7 @@ dependencies = [
"argh", "argh",
"askama_escape", "askama_escape",
"async-trait", "async-trait",
"chrono",
"dioxus", "dioxus",
"dioxus-core", "dioxus-core",
"dioxus-html", "dioxus-html",
+2 -2
View File
@@ -367,8 +367,8 @@ async fn start_server(
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
PluginManager::on_serve_start(_config)?; PluginManager::on_serve_start(_config)?;
// Parse address // Bind the server to `[::]` and it will LISTEN for both IPv4 and IPv6. (required IPv6 dual stack)
let addr = format!("0.0.0.0:{}", port).parse().unwrap(); let addr = format!("[::]:{}", port).parse().unwrap();
// Open the browser // Open the browser
if start_browser { if start_browser {
+6 -3
View File
@@ -38,14 +38,13 @@ dioxus-mobile = { workspace = true, optional = true }
tracing = { workspace = true } tracing = { workspace = true }
tracing-futures = { workspace = true, optional = true } tracing-futures = { workspace = true, optional = true }
once_cell = "1.17.1" once_cell = "1.17.1"
tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true }
tokio-util = { version = "0.7.8", features = ["rt"], optional = true } tokio-util = { version = "0.7.8", features = ["rt"], optional = true }
anymap = { version = "0.12.1", optional = true } anymap = { version = "0.12.1", optional = true }
serde = "1.0.159" serde = "1.0.159"
serde_json = { version = "1.0.95", optional = true } serde_json = { version = "1.0.95", optional = true }
tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } tokio-stream = { version = "0.1.12", features = ["sync"], optional = true }
futures-util = { workspace = true, default-features = false, optional = true } futures-util = { workspace = true, default-features = false }
ciborium = "0.2.1" ciborium = "0.2.1"
base64 = "0.21.0" base64 = "0.21.0"
@@ -59,12 +58,16 @@ web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document
dioxus-cli-config = { workspace = true, optional = true } dioxus-cli-config = { workspace = true, optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { workspace = true, features = ["rt", "sync"], optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
dioxus-hot-reload = { workspace = true } dioxus-hot-reload = { workspace = true }
tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true }
[features] [features]
default = ["hot-reload"] default = ["hot-reload"]
hot-reload = ["serde_json", "futures-util"] hot-reload = ["serde_json"]
web = ["dioxus-web", "web-sys"] web = ["dioxus-web", "web-sys"]
desktop = ["dioxus-desktop"] desktop = ["dioxus-desktop"]
mobile = ["dioxus-mobile"] mobile = ["dioxus-mobile"]
+28 -35
View File
@@ -65,8 +65,7 @@ use axum::{
}; };
use dioxus_lib::prelude::VirtualDom; use dioxus_lib::prelude::VirtualDom;
use http::header::*; use http::header::*;
use server_fn::error::NoCustomError;
use server_fn::error::ServerFnErrorSerde;
use std::sync::Arc; use std::sync::Arc;
use crate::{ use crate::{
@@ -453,20 +452,6 @@ pub async fn hot_reload_handler(ws: axum::extract::WebSocketUpgrade) -> impl Int
}) })
} }
fn get_local_pool() -> tokio_util::task::LocalPoolHandle {
use once_cell::sync::OnceCell;
static LOCAL_POOL: OnceCell<tokio_util::task::LocalPoolHandle> = OnceCell::new();
LOCAL_POOL
.get_or_init(|| {
tokio_util::task::LocalPoolHandle::new(
std::thread::available_parallelism()
.map(Into::into)
.unwrap_or(1),
)
})
.clone()
}
/// A handler for Dioxus server functions. This will run the server function and return the result. /// A handler for Dioxus server functions. This will run the server function and return the result.
async fn handle_server_fns_inner( async fn handle_server_fns_inner(
path: &str, path: &str,
@@ -475,15 +460,13 @@ async fn handle_server_fns_inner(
) -> impl IntoResponse { ) -> impl IntoResponse {
use server_fn::middleware::Service; use server_fn::middleware::Service;
let (tx, rx) = tokio::sync::oneshot::channel();
let path_string = path.to_string(); let path_string = path.to_string();
get_local_pool().spawn_pinned(move || async move { let future = move || async move {
let (parts, body) = req.into_parts(); let (parts, body) = req.into_parts();
let req = Request::from_parts(parts.clone(), body); let req = Request::from_parts(parts.clone(), body);
if let Some(mut service) =
let res = if let Some(mut service) =
server_fn::axum::get_server_fn_service(&path_string) server_fn::axum::get_server_fn_service(&path_string)
{ {
@@ -538,18 +521,28 @@ async fn handle_server_fns_inner(
} }
) )
} }
.expect("could not build Response"); .expect("could not build Response")
};
#[cfg(target_arch = "wasm32")]
{
use futures_util::future::FutureExt;
_ = tx.send(res); let result = tokio::task::spawn_local(future);
}); let result = result.then(|f| async move { f.unwrap() });
result.await.unwrap_or_else(|e| {
rx.await.unwrap_or_else(|e| { use server_fn::error::NoCustomError;
( use server_fn::error::ServerFnErrorSerde;
StatusCode::INTERNAL_SERVER_ERROR, (
ServerFnError::<NoCustomError>::ServerError(e.to_string()) StatusCode::INTERNAL_SERVER_ERROR,
.ser() ServerFnError::<NoCustomError>::ServerError(e.to_string())
.unwrap_or_default(), .ser()
) .unwrap_or_default(),
.into_response() )
}) .into_response()
})
}
#[cfg(not(target_arch = "wasm32"))]
{
future().await
}
} }
+1 -1
View File
@@ -21,7 +21,7 @@ pub fn launch(
vdom vdom
}; };
#[cfg(feature = "server")] #[cfg(all(feature = "server", not(target_arch = "wasm32")))]
tokio::runtime::Runtime::new() tokio::runtime::Runtime::new()
.unwrap() .unwrap()
.block_on(async move { .block_on(async move {
+6 -1
View File
@@ -16,7 +16,12 @@ mod config;
mod hooks; mod hooks;
pub mod launch; pub mod launch;
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "server"))] #[cfg(all(
debug_assertions,
feature = "hot-reload",
feature = "server",
not(target_arch = "wasm32")
))]
mod hot_reload; mod hot_reload;
pub use config::*; pub use config::*;
+97 -89
View File
@@ -6,13 +6,33 @@ use dioxus_ssr::{
incremental::{IncrementalRendererConfig, RenderFreshness, WrapBody}, incremental::{IncrementalRendererConfig, RenderFreshness, WrapBody},
Renderer, Renderer,
}; };
use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
use tokio::task::spawn_blocking; use tokio::task::JoinHandle;
use crate::prelude::*; use crate::prelude::*;
use dioxus_lib::prelude::*; use dioxus_lib::prelude::*;
fn spawn_platform<Fut>(f: impl FnOnce() -> Fut + Send + 'static) -> JoinHandle<Fut::Output>
where
Fut: Future + 'static,
Fut::Output: Send + 'static,
{
#[cfg(not(target_arch = "wasm32"))]
{
tokio::task::spawn_blocking(move || {
tokio::runtime::Runtime::new()
.expect("couldn't spawn runtime")
.block_on(f())
})
}
#[cfg(target_arch = "wasm32")]
{
tokio::task::spawn_local(f())
}
}
enum SsrRendererPool { enum SsrRendererPool {
Renderer(RwLock<Vec<Renderer>>), Renderer(RwLock<Vec<Renderer>>),
Incremental(RwLock<Vec<dioxus_ssr::incremental::IncrementalRenderer>>), Incremental(RwLock<Vec<dioxus_ssr::incremental::IncrementalRenderer>>),
@@ -37,53 +57,45 @@ impl SsrRendererPool {
let (tx, rx) = tokio::sync::oneshot::channel(); let (tx, rx) = tokio::sync::oneshot::channel();
spawn_blocking(move || { spawn_platform(move || async move {
tokio::runtime::Runtime::new() let mut vdom = virtual_dom_factory();
.expect("couldn't spawn runtime") let mut to = WriteBuffer { buffer: Vec::new() };
.block_on(async move { // before polling the future, we need to set the context
let mut vdom = virtual_dom_factory(); let prev_context = SERVER_CONTEXT.with(|ctx| ctx.replace(server_context));
let mut to = WriteBuffer { buffer: Vec::new() }; // poll the future, which may call server_context()
// before polling the future, we need to set the context tracing::info!("Rebuilding vdom");
let prev_context = vdom.rebuild(&mut NoOpMutations);
SERVER_CONTEXT.with(|ctx| ctx.replace(server_context)); vdom.wait_for_suspense().await;
// poll the future, which may call server_context() tracing::info!("Suspense resolved");
tracing::info!("Rebuilding vdom"); // after polling the future, we need to restore the context
vdom.rebuild(&mut NoOpMutations); SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
vdom.wait_for_suspense().await;
tracing::info!("Suspense resolved");
// after polling the future, we need to restore the context
SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
if let Err(err) = wrapper.render_before_body(&mut *to) { if let Err(err) = wrapper.render_before_body(&mut *to) {
let _ = tx.send(Err(err)); let _ = tx.send(Err(err));
return; return;
} }
if let Err(err) = renderer.render_to(&mut to, &vdom) { if let Err(err) = renderer.render_to(&mut to, &vdom) {
let _ = tx.send(Err( let _ = tx.send(Err(
dioxus_ssr::incremental::IncrementalRendererError::RenderError( dioxus_ssr::incremental::IncrementalRendererError::RenderError(err),
err, ));
), return;
)); }
return; if let Err(err) = wrapper.render_after_body(&mut *to) {
} let _ = tx.send(Err(err));
if let Err(err) = wrapper.render_after_body(&mut *to) { return;
let _ = tx.send(Err(err)); }
return; match String::from_utf8(to.buffer) {
} Ok(html) => {
match String::from_utf8(to.buffer) { let _ = tx.send(Ok((renderer, RenderFreshness::now(None), html)));
Ok(html) => { }
let _ = Err(err) => {
tx.send(Ok((renderer, RenderFreshness::now(None), html))); _ = tx.send(Err(
} dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new(
Err(err) => { err,
_ = tx.send(Err( )),
dioxus_ssr::incremental::IncrementalRendererError::Other( ));
Box::new(err), }
), }
));
}
}
});
}); });
let (renderer, freshness, html) = rx.await.unwrap()?; let (renderer, freshness, html) = rx.await.unwrap()?;
pool.write().unwrap().push(renderer); pool.write().unwrap().push(renderer);
@@ -98,53 +110,49 @@ impl SsrRendererPool {
let (tx, rx) = tokio::sync::oneshot::channel(); let (tx, rx) = tokio::sync::oneshot::channel();
let server_context = server_context.clone(); let server_context = server_context.clone();
spawn_blocking(move || { spawn_platform(move || async move {
tokio::runtime::Runtime::new() let mut to = WriteBuffer { buffer: Vec::new() };
.expect("couldn't spawn runtime") match renderer
.block_on(async move { .render(
let mut to = WriteBuffer { buffer: Vec::new() }; route,
match renderer virtual_dom_factory,
.render( &mut *to,
route, |vdom| {
virtual_dom_factory, Box::pin(async move {
&mut *to, // before polling the future, we need to set the context
|vdom| { let prev_context = SERVER_CONTEXT
Box::pin(async move { .with(|ctx| ctx.replace(Box::new(server_context)));
// before polling the future, we need to set the context // poll the future, which may call server_context()
let prev_context = SERVER_CONTEXT tracing::info!("Rebuilding vdom");
.with(|ctx| ctx.replace(Box::new(server_context))); vdom.rebuild(&mut NoOpMutations);
// poll the future, which may call server_context() vdom.wait_for_suspense().await;
tracing::info!("Rebuilding vdom"); tracing::info!("Suspense resolved");
vdom.rebuild(&mut NoOpMutations); // after polling the future, we need to restore the context
vdom.wait_for_suspense().await; SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context));
tracing::info!("Suspense resolved"); })
// after polling the future, we need to restore the context },
SERVER_CONTEXT.with(|ctx| ctx.replace(prev_context)); &wrapper,
}) )
}, .await
&wrapper, {
) Ok(freshness) => {
.await match String::from_utf8(to.buffer).map_err(|err| {
{ dioxus_ssr::incremental::IncrementalRendererError::Other(Box::new(
Ok(freshness) => { err,
match String::from_utf8(to.buffer).map_err(|err| { ))
dioxus_ssr::incremental::IncrementalRendererError::Other( }) {
Box::new(err), Ok(html) => {
) let _ = tx.send(Ok((freshness, html)));
}) {
Ok(html) => {
let _ = tx.send(Ok((freshness, html)));
}
Err(err) => {
let _ = tx.send(Err(err));
}
}
} }
Err(err) => { Err(err) => {
let _ = tx.send(Err(err)); let _ = tx.send(Err(err));
} }
} }
}) }
Err(err) => {
let _ = tx.send(Err(err));
}
}
}); });
let (freshness, html) = rx.await.unwrap()?; let (freshness, html) = rx.await.unwrap()?;
+17 -2
View File
@@ -9,6 +9,7 @@ use std::path::PathBuf;
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct ServeConfigBuilder { pub struct ServeConfigBuilder {
pub(crate) root_id: Option<&'static str>, pub(crate) root_id: Option<&'static str>,
pub(crate) index_html: Option<String>,
pub(crate) index_path: Option<PathBuf>, pub(crate) index_path: Option<PathBuf>,
pub(crate) assets_path: Option<PathBuf>, pub(crate) assets_path: Option<PathBuf>,
pub(crate) incremental: pub(crate) incremental:
@@ -40,6 +41,7 @@ impl ServeConfigBuilder {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
root_id: None, root_id: None,
index_html: None,
index_path: None, index_path: None,
assets_path: None, assets_path: None,
incremental: None, incremental: None,
@@ -52,6 +54,12 @@ impl ServeConfigBuilder {
self self
} }
/// Set the contents of the index.html file to be served. (precedence over index_path)
pub fn index_html(mut self, index_html: String) -> Self {
self.index_html = Some(index_html);
self
}
/// Set the path of the index.html file to be served. (defaults to {assets_path}/index.html) /// Set the path of the index.html file to be served. (defaults to {assets_path}/index.html)
pub fn index_path(mut self, index_path: PathBuf) -> Self { pub fn index_path(mut self, index_path: PathBuf) -> Self {
self.index_path = Some(index_path); self.index_path = Some(index_path);
@@ -86,8 +94,11 @@ impl ServeConfigBuilder {
let root_id = self.root_id.unwrap_or("main"); let root_id = self.root_id.unwrap_or("main");
let index = load_index_html(index_path, root_id); let index_html = self
.index_html
.unwrap_or_else(|| load_index_path(index_path));
let index = load_index_html(index_html, root_id);
ServeConfig { ServeConfig {
index, index,
assets_path, assets_path,
@@ -96,13 +107,16 @@ impl ServeConfigBuilder {
} }
} }
fn load_index_html(path: PathBuf, root_id: &'static str) -> IndexHtml { fn load_index_path(path: PathBuf) -> String {
let mut file = File::open(path).expect("Failed to find index.html. Make sure the index_path is set correctly and the WASM application has been built."); let mut file = File::open(path).expect("Failed to find index.html. Make sure the index_path is set correctly and the WASM application has been built.");
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents) file.read_to_string(&mut contents)
.expect("Failed to read index.html"); .expect("Failed to read index.html");
contents
}
fn load_index_html(contents: String, root_id: &'static str) -> IndexHtml {
let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")).unwrap_or_else(|| panic!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page.")); let (pre_main, post_main) = contents.split_once(&format!("id=\"{root_id}\"")).unwrap_or_else(|| panic!("Failed to find id=\"{root_id}\" in index.html. The id is used to inject the application into the page."));
let post_main = post_main.split_once('>').unwrap_or_else(|| { let post_main = post_main.split_once('>').unwrap_or_else(|| {
@@ -131,6 +145,7 @@ pub(crate) struct IndexHtml {
#[derive(Clone)] #[derive(Clone)]
pub struct ServeConfig { pub struct ServeConfig {
pub(crate) index: IndexHtml, pub(crate) index: IndexHtml,
#[allow(dead_code)]
pub(crate) assets_path: PathBuf, pub(crate) assets_path: PathBuf,
pub(crate) incremental: pub(crate) incremental:
Option<std::sync::Arc<dioxus_ssr::incremental::IncrementalRendererConfig>>, Option<std::sync::Arc<dioxus_ssr::incremental::IncrementalRendererConfig>>,
+8 -2
View File
@@ -18,9 +18,15 @@ rustc-hash = "1.1.0"
lru = { workspace = true } lru = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
http = { workspace = true } http = { workspace = true }
tokio = { version = "1.28", features = ["fs", "io-util"], optional = true }
async-trait = { workspace = true } async-trait = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
chrono = { verison = "0.4.34", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
tokio = { version = "1.28", features = ["io-util"], optional = true }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1.28", features = ["fs", "io-util"], optional = true }
[dev-dependencies] [dev-dependencies]
dioxus = { workspace = true } dioxus = { workspace = true }
@@ -36,4 +42,4 @@ fs_extra = "1.2.0"
[features] [features]
default = [] default = []
incremental = ["dep:tokio"] incremental = ["dep:tokio", "chrono"]
+1
View File
@@ -103,6 +103,7 @@ impl ValidCachedPath {
}) })
} }
#[cfg(not(target_arch = "wasm32"))]
pub fn freshness(&self, max_age: Option<std::time::Duration>) -> Option<RenderFreshness> { pub fn freshness(&self, max_age: Option<std::time::Duration>) -> Option<RenderFreshness> {
let age = self.timestamp.elapsed().ok()?.as_secs(); let age = self.timestamp.elapsed().ok()?.as_secs();
let max_age = max_age.map(|max_age| max_age.as_secs()); let max_age = max_age.map(|max_age| max_age.as_secs());
+32 -23
View File
@@ -3,18 +3,19 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use crate::fs_cache::ValidCachedPath; use crate::fs_cache::ValidCachedPath;
use chrono::offset::Utc;
use chrono::DateTime;
use dioxus_core::VirtualDom; use dioxus_core::VirtualDom;
use rustc_hash::FxHasher; use rustc_hash::FxHasher;
use std::{ use std::{
future::Future, future::Future,
hash::BuildHasherDefault, hash::BuildHasherDefault,
io::Write,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
path::PathBuf, path::PathBuf,
pin::Pin, pin::Pin,
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
use tokio::io::{AsyncWrite, AsyncWriteExt, BufReader}; use tokio::io::{AsyncWrite, AsyncWriteExt};
pub use crate::fs_cache::*; pub use crate::fs_cache::*;
pub use crate::incremental_cfg::*; pub use crate::incremental_cfg::*;
@@ -24,7 +25,7 @@ pub struct IncrementalRenderer {
pub(crate) static_dir: PathBuf, pub(crate) static_dir: PathBuf,
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
pub(crate) memory_cache: pub(crate) memory_cache:
Option<lru::LruCache<String, (SystemTime, Vec<u8>), BuildHasherDefault<FxHasher>>>, Option<lru::LruCache<String, (DateTime<Utc>, Vec<u8>), BuildHasherDefault<FxHasher>>>,
pub(crate) invalidate_after: Option<Duration>, pub(crate) invalidate_after: Option<Duration>,
pub(crate) ssr_renderer: crate::Renderer, pub(crate) ssr_renderer: crate::Renderer,
pub(crate) map_path: PathMapFn, pub(crate) map_path: PathMapFn,
@@ -65,6 +66,7 @@ impl IncrementalRenderer {
let _ = std::fs::remove_dir_all(&self.static_dir); let _ = std::fs::remove_dir_all(&self.static_dir);
} }
#[cfg(not(target_arch = "wasm32"))]
fn track_timestamps(&self) -> bool { fn track_timestamps(&self) -> bool {
self.invalidate_after.is_some() self.invalidate_after.is_some()
} }
@@ -98,25 +100,30 @@ impl IncrementalRenderer {
route: String, route: String,
html: Vec<u8>, html: Vec<u8>,
) -> Result<RenderFreshness, IncrementalRendererError> { ) -> Result<RenderFreshness, IncrementalRendererError> {
let file_path = self.route_as_path(&route); #[cfg(not(target_arch = "wasm32"))]
if let Some(parent) = file_path.parent() { {
if !parent.exists() { use std::io::Write;
std::fs::create_dir_all(parent)?; let file_path = self.route_as_path(&route);
if let Some(parent) = file_path.parent() {
if !parent.exists() {
std::fs::create_dir_all(parent)?;
}
} }
let file = std::fs::File::create(file_path)?;
let mut file = std::io::BufWriter::new(file);
file.write_all(&html)?;
} }
let file = std::fs::File::create(file_path)?;
let mut file = std::io::BufWriter::new(file);
file.write_all(&html)?;
self.add_to_memory_cache(route, html); self.add_to_memory_cache(route, html);
Ok(RenderFreshness::now(self.invalidate_after)) Ok(RenderFreshness::now(self.invalidate_after))
} }
fn add_to_memory_cache(&mut self, route: String, html: Vec<u8>) { fn add_to_memory_cache(&mut self, route: String, html: Vec<u8>) {
if let Some(cache) = self.memory_cache.as_mut() { if let Some(cache) = self.memory_cache.as_mut() {
cache.put(route, (SystemTime::now(), html)); cache.put(route, (Utc::now(), html));
} }
} }
#[cfg(not(target_arch = "wasm32"))]
fn promote_memory_cache<K: AsRef<str>>(&mut self, route: K) { fn promote_memory_cache<K: AsRef<str>>(&mut self, route: K) {
if let Some(cache) = self.memory_cache.as_mut() { if let Some(cache) = self.memory_cache.as_mut() {
cache.promote(route.as_ref()) cache.promote(route.as_ref())
@@ -134,27 +141,28 @@ impl IncrementalRenderer {
.as_mut() .as_mut()
.and_then(|cache| cache.get(&route)) .and_then(|cache| cache.get(&route))
{ {
if let Ok(elapsed) = timestamp.elapsed() { let now = Utc::now();
let age = elapsed.as_secs(); let elapsed = timestamp.signed_duration_since(now);
if let Some(invalidate_after) = self.invalidate_after { let age = elapsed.num_seconds();
if elapsed < invalidate_after { if let Some(invalidate_after) = self.invalidate_after {
tracing::trace!("memory cache hit {:?}", route); if elapsed.to_std().unwrap() < invalidate_after {
output.write_all(cache_hit).await?;
let max_age = invalidate_after.as_secs();
return Ok(Some(RenderFreshness::new(age, max_age)));
}
} else {
tracing::trace!("memory cache hit {:?}", route); tracing::trace!("memory cache hit {:?}", route);
output.write_all(cache_hit).await?; output.write_all(cache_hit).await?;
return Ok(Some(RenderFreshness::new_age(age))); let max_age = invalidate_after.as_secs();
return Ok(Some(RenderFreshness::new(age as u64, max_age)));
} }
} else {
tracing::trace!("memory cache hit {:?}", route);
output.write_all(cache_hit).await?;
return Ok(Some(RenderFreshness::new_age(age as u64)));
} }
} }
// check the file cache // check the file cache
#[cfg(not(target_arch = "wasm32"))]
if let Some(file_path) = self.find_file(&route) { if let Some(file_path) = self.find_file(&route) {
if let Some(freshness) = file_path.freshness(self.invalidate_after) { if let Some(freshness) = file_path.freshness(self.invalidate_after) {
if let Ok(file) = tokio::fs::File::open(file_path.full_path).await { if let Ok(file) = tokio::fs::File::open(file_path.full_path).await {
let mut file = BufReader::new(file); let mut file = tokio::io::BufReader::new(file);
tokio::io::copy_buf(&mut file, output).await?; tokio::io::copy_buf(&mut file, output).await?;
tracing::trace!("file cache hit {:?}", route); tracing::trace!("file cache hit {:?}", route);
self.promote_memory_cache(&route); self.promote_memory_cache(&route);
@@ -223,6 +231,7 @@ impl IncrementalRenderer {
} }
} }
#[cfg(not(target_arch = "wasm32"))]
fn route_as_path(&self, route: &str) -> PathBuf { fn route_as_path(&self, route: &str) -> PathBuf {
let mut file_path = (self.map_path)(route); let mut file_path = (self.map_path)(route);
if self.track_timestamps() { if self.track_timestamps() {