diff --git a/crates/tuono/src/commands/dev.rs b/crates/tuono/src/commands/dev.rs index 37f8c0f1..2083cfde 100644 --- a/crates/tuono/src/commands/dev.rs +++ b/crates/tuono/src/commands/dev.rs @@ -116,7 +116,7 @@ pub async fn watch(source_builder: SourceBuilder) -> Result<()> { // Remove the spinner sp.stop(); - term.clear_line().unwrap(); + _ = term.clear_line(); let wx = Watchexec::new(move |mut action| { let mut should_reload_ssr_bundle = false; @@ -126,6 +126,8 @@ pub async fn watch(source_builder: SourceBuilder) -> Result<()> { for path in event.paths() { let file_path = path.0.to_string_lossy(); if file_path.ends_with(".rs") { + // TODO: only the new file show trigger a axum refresh. + // Update of existing file should just reload the server. should_reload_rust_server = true } @@ -138,9 +140,10 @@ pub async fn watch(source_builder: SourceBuilder) -> Result<()> { if should_reload_rust_server { println!(" Reloading..."); rust_server.stop(); - let mut builder = source_builder.write().unwrap(); - builder.app.collect_routes(); - _ = builder.refresh_axum_source(); + if let Ok(mut builder) = source_builder.write() { + builder.app.collect_routes(); + _ = builder.refresh_axum_source(); + } rust_server.start(); } diff --git a/crates/tuono/src/source_builder.rs b/crates/tuono/src/source_builder.rs index d4625119..ebc292ad 100644 --- a/crates/tuono/src/source_builder.rs +++ b/crates/tuono/src/source_builder.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::fs; use std::io; use std::io::prelude::*; @@ -77,8 +76,6 @@ impl SourceBuilder { let base_path = std::env::current_dir()?; - create_client_entry_files()?; - Ok(Self { app, mode, @@ -91,11 +88,14 @@ impl SourceBuilder { let Self { mode, .. } = &self; self.refresh_axum_source()?; + let dev_folder = Path::new(DEV_FOLDER); + self.create_file(dev_folder.join("server-main.tsx"), SERVER_ENTRY_DATA)?; + self.create_file(dev_folder.join("client-main.tsx"), CLIENT_ENTRY_DATA)?; if mode == &Mode::Dev { self.app.build_tuono_config()?; - let fallback_html = create_html_fallback(&self.app); - self.create_file(FALLBACK_HTML_PATH, &fallback_html)?; + let fallback_html = self.build_html_fallback(); + self.create_file(PathBuf::from(FALLBACK_HTML_PATH), &fallback_html)?; } Ok(()) @@ -106,14 +106,8 @@ impl SourceBuilder { let src = AXUM_ENTRY_POINT .replace("\r", "") - .replace( - "// ROUTE_BUILDER\n", - &create_routes_declaration(&app.route_map), - ) - .replace( - "// MODULE_IMPORTS\n", - &create_modules_declaration(&app.route_map), - ) + .replace("// ROUTE_BUILDER\n", &self.create_routes_declaration()) + .replace("// MODULE_IMPORTS\n", &self.create_modules_declaration()) .replace("/*VERSION*/", crate_version!()) .replace("/*MODE*/", mode.as_str()) .replace( @@ -158,97 +152,87 @@ impl SourceBuilder { pub fn refresh_axum_source(&self) -> io::Result<()> { let axum_source = self.generate_axum_source(); - self.create_file(MAIN_FILE_PATH, &axum_source)?; + self.create_file(PathBuf::from(MAIN_FILE_PATH), &axum_source)?; Ok(()) } - fn create_file(&self, path: &str, content: &str) -> io::Result<()> { + fn create_file(&self, path: PathBuf, content: &str) -> io::Result<()> { let mut data_file = fs::File::create(self.base_path.join(path))?; data_file.write_all(content.as_bytes())?; Ok(()) } -} -fn create_routes_declaration(routes: &HashMap) -> String { - let mut route_declarations = String::from("// ROUTE_BUILDER\n"); + fn create_routes_declaration(&self) -> String { + let routes = &self.app.route_map; + let mut route_declarations = String::from("// ROUTE_BUILDER\n"); - for (_, route) in routes.iter() { - let Route { axum_info, .. } = &route; + for (_, route) in routes.iter() { + let Route { axum_info, .. } = &route; - if axum_info.is_some() { - let AxumInfo { - axum_route, - module_import, - } = axum_info.as_ref().unwrap(); + if axum_info.is_some() { + let AxumInfo { + axum_route, + module_import, + } = axum_info.as_ref().unwrap(); - if !route.is_api() { - route_declarations.push_str(&format!( - r#".route("{axum_route}", get({module_import}::tuono_internal_route))"# - )); - - route_declarations.push_str(&format!( - r#".route("/__tuono/data{axum_route}", get({module_import}::tuono_internal_api))"# - )); - } else { - for method in route.api_data.as_ref().unwrap().methods.clone() { - let method = method.to_string().to_lowercase(); + if !route.is_api() { route_declarations.push_str(&format!( - r#".route("{axum_route}", {method}({module_import}::{method}_tuono_internal_api))"# + r#".route("{axum_route}", get({module_import}::tuono_internal_route))"# )); + + route_declarations.push_str(&format!( + r#".route("/__tuono/data{axum_route}", get({module_import}::tuono_internal_api))"# + )); + } else { + for method in route.api_data.as_ref().unwrap().methods.clone() { + let method = method.to_string().to_lowercase(); + route_declarations.push_str(&format!( + r#".route("{axum_route}", {method}({module_import}::{method}_tuono_internal_api))"# + )); + } } } } + + route_declarations } - route_declarations -} + fn create_modules_declaration(&self) -> String { + let routes = &self.app.route_map; + let mut route_declarations = String::from("// MODULE_IMPORTS\n"); -fn create_modules_declaration(routes: &HashMap) -> String { - let mut route_declarations = String::from("// MODULE_IMPORTS\n"); + for (path, route) in routes.iter() { + if route.axum_info.is_some() { + let AxumInfo { module_import, .. } = route.axum_info.as_ref().unwrap(); - for (path, route) in routes.iter() { - if route.axum_info.is_some() { - let AxumInfo { module_import, .. } = route.axum_info.as_ref().unwrap(); - - route_declarations.push_str(&format!( - r#"#[path="../{ROUTE_FOLDER}{path}.rs"] + route_declarations.push_str(&format!( + r#"#[path="../{ROUTE_FOLDER}{path}.rs"] mod {module_import}; "# - )) + )) + } } + + route_declarations } - route_declarations -} - -fn create_html_fallback(app: &App) -> String { - if let Some(config) = app.config.as_ref() { - if let Some(origin) = &config.server.origin { - FALLBACK_HTML.replace("[BASE_URL]", origin) + fn build_html_fallback(&self) -> String { + if let Some(config) = &self.app.config.as_ref() { + if let Some(origin) = &config.server.origin { + FALLBACK_HTML.replace("[BASE_URL]", origin) + } else { + let url = format!("http://{}:{}", config.server.host, config.server.port); + FALLBACK_HTML.replace("[BASE_URL]", url.as_str()) + } } else { - let url = format!("http://{}:{}", config.server.host, config.server.port); - FALLBACK_HTML.replace("[BASE_URL]", url.as_str()) + "".to_string() } - } else { - "".to_string() } } -pub fn create_client_entry_files() -> io::Result<()> { - let dev_folder = Path::new(DEV_FOLDER); - - let mut server_entry = fs::File::create(dev_folder.join("server-main.tsx"))?; - let mut client_entry = fs::File::create(dev_folder.join("client-main.tsx"))?; - - server_entry.write_all(SERVER_ENTRY_DATA.as_bytes())?; - client_entry.write_all(CLIENT_ENTRY_DATA.as_bytes())?; - - Ok(()) -} - #[cfg(test)] mod tests { @@ -312,7 +296,13 @@ mod tests { let mut app = App::new(); app.config = Some(Default::default()); - let fallback_html = create_html_fallback(&app); + let source_builder = SourceBuilder { + app, + mode: Mode::Dev, + base_path: PathBuf::new(), + }; + + let fallback_html = source_builder.build_html_fallback(); assert!(fallback_html.contains("http://localhost:3000/vite-server/@react-refresh")); assert!(fallback_html.contains("http://localhost:3000/vite-server/@vite/client"));