diff --git a/crates/tuono/Cargo.toml b/crates/tuono/Cargo.toml index 8961002b..c7377744 100644 --- a/crates/tuono/Cargo.toml +++ b/crates/tuono/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuono" -version = "0.3.0" +version = "0.3.1" edition = "2021" authors = ["V. Ageno "] description = "The react/rust fullstack framework" diff --git a/crates/tuono/src/source_builder.rs b/crates/tuono/src/source_builder.rs index a52fb97b..9440b59c 100644 --- a/crates/tuono/src/source_builder.rs +++ b/crates/tuono/src/source_builder.rs @@ -47,12 +47,7 @@ pub const AXUM_ENTRY_POINT: &str = r##" // File automatically generated // Do not manually change it -use axum::extract::{Path, Request}; -use axum::response::Html; -use axum::{routing::get, Router}; -use tower_http::services::ServeDir; -use std::collections::HashMap; -use tuono_lib::{ssr, Ssr, Mode, GLOBAL_MODE, manifest::load_manifest}; +use tuono_lib::{tokio, Mode, Server, axum::Router, axum::routing::get}; use reqwest::Client; const MODE: Mode = /*MODE*/; @@ -61,56 +56,18 @@ const MODE: Mode = /*MODE*/; #[tokio::main] async fn main() { - Ssr::create_platform(); - let fetch = Client::new(); - GLOBAL_MODE.set(MODE).unwrap(); - - if MODE == Mode::Prod { - load_manifest() - } - - let app = Router::new() + let router = Router::new() // ROUTE_BUILDER - .fallback_service(ServeDir::new("/*public_dir*/").fallback(get(catch_all))) .with_state(fetch); - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); - - if MODE == Mode::Dev { - println!("\nDevelopment app ready at http://localhost:3000/"); - } else { - println!("\nProduction app ready at http://localhost:3000/"); - } - axum::serve(listener, app).await.unwrap(); -} - -async fn catch_all(Path(params): Path>, request: Request) -> Html { - let pathname = &request.uri(); - let headers = &request.headers(); - - let req = tuono_lib::Request::new(pathname, headers, params); - - - // TODO: remove unwrap - let payload = tuono_lib::Payload::new(&req, &"") - .client_payload() - .unwrap(); - - let result = ssr::Js::SSR.with(|ssr| ssr.borrow_mut().render_to_string(Some(&payload))); - - match result { - Ok(html) => Html(html), - _ => Html("500 internal server error".to_string()), - } + Server::init(router, MODE).start().await } "##; const ROOT_FOLDER: &str = "src/routes"; const DEV_FOLDER: &str = ".tuono"; -const DEV_PUBLIC_DIR: &str = "public"; -const PROD_PUBLIC_DIR: &str = "out/client"; #[derive(Debug, PartialEq, Eq)] struct Route { @@ -254,12 +211,6 @@ pub fn bundle_axum_source(mode: Mode) -> io::Result<()> { } fn generate_axum_source(source_builder: &SourceBuilder, mode: Mode) -> String { - let public_dir = if mode == Mode::Prod { - PROD_PUBLIC_DIR - } else { - DEV_PUBLIC_DIR - }; - AXUM_ENTRY_POINT .replace( "// ROUTE_BUILDER\n", @@ -269,7 +220,6 @@ fn generate_axum_source(source_builder: &SourceBuilder, mode: Mode) -> String { "// MODULE_IMPORTS\n", &create_modules_declaration(&source_builder.route_map), ) - .replace("/*public_dir*/", public_dir) .replace("/*MODE*/", mode.as_str()) } @@ -412,20 +362,4 @@ mod tests { assert_eq!(dev, "Mode::Dev"); assert_eq!(prod, "Mode::Prod"); } - - #[test] - fn should_replace_the_correct_public_folder_dev() { - let source_builder = SourceBuilder::new(); - let source = generate_axum_source(&source_builder, Mode::Dev); - - assert!(source.contains(r#"ServeDir::new("public")"#)) - } - - #[test] - fn should_replace_the_correct_public_folder_prod() { - let source_builder = SourceBuilder::new(); - let source = generate_axum_source(&source_builder, Mode::Prod); - - assert!(source.contains(r#"ServeDir::new("out/client")"#)) - } } diff --git a/crates/tuono_lib/Cargo.toml b/crates/tuono_lib/Cargo.toml index 67a59fb3..fd5320bf 100644 --- a/crates/tuono_lib/Cargo.toml +++ b/crates/tuono_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuono_lib" -version = "0.3.0" +version = "0.3.1" edition = "2021" authors = ["V. Ageno "] description = "The react/rust fullstack framework" @@ -19,14 +19,16 @@ path = "src/lib.rs" [dependencies] ssr_rs = "0.5.5" -axum = "0.7.5" +axum = {version = "0.7.5", features = ["json"]} +tokio = { version = "1.37.0", features = ["full"] } serde = { version = "1.0.202", features = ["derive"] } erased-serde = "0.4.5" serde_json = "1.0" -tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.3.0"} +tuono_lib_macros = {path = "../tuono_lib_macros", version = "0.3.1"} once_cell = "1.19.0" lazy_static = "1.5.0" regex = "1.10.5" either = "1.13.0" +tower-http = {version = "0.5.2", features = ["fs"]} diff --git a/crates/tuono_lib/src/catch_all.rs b/crates/tuono_lib/src/catch_all.rs new file mode 100644 index 00000000..1f6dd743 --- /dev/null +++ b/crates/tuono_lib/src/catch_all.rs @@ -0,0 +1,24 @@ +use crate::{ssr::Js, Payload}; +use axum::extract::{Path, Request}; +use axum::response::Html; +use std::collections::HashMap; + +pub async fn catch_all( + Path(params): Path>, + request: Request, +) -> Html { + let pathname = &request.uri(); + let headers = &request.headers(); + + let req = crate::Request::new(pathname, headers, params); + + // TODO: remove unwrap + let payload = Payload::new(&req, &"").client_payload().unwrap(); + + let result = Js::SSR.with(|ssr| ssr.borrow_mut().render_to_string(Some(&payload))); + + match result { + Ok(html) => Html(html), + _ => Html("500 internal server error".to_string()), + } +} diff --git a/crates/tuono_lib/src/lib.rs b/crates/tuono_lib/src/lib.rs index ac224d28..94b588e6 100644 --- a/crates/tuono_lib/src/lib.rs +++ b/crates/tuono_lib/src/lib.rs @@ -1,14 +1,19 @@ -pub mod manifest; +mod catch_all; +mod manifest; mod mode; mod payload; mod request; mod response; +mod server; +mod ssr; -pub mod ssr; - -pub use mode::{Mode, GLOBAL_MODE}; +pub use mode::Mode; pub use payload::Payload; pub use request::Request; pub use response::{Props, Response}; -pub use ssr_rs::Ssr; +pub use server::Server; pub use tuono_lib_macros::handler; + +// Re-exports +pub use axum; +pub use tokio; diff --git a/crates/tuono_lib/src/payload.rs b/crates/tuono_lib/src/payload.rs index 8277c49c..2a654c2f 100644 --- a/crates/tuono_lib/src/payload.rs +++ b/crates/tuono_lib/src/payload.rs @@ -1,4 +1,5 @@ -use crate::{manifest::MANIFEST, Mode, GLOBAL_MODE}; +use crate::manifest::MANIFEST; +use crate::mode::{Mode, GLOBAL_MODE}; use erased_serde::Serialize; use regex::Regex; use serde::Serialize as SerdeSerialize; diff --git a/crates/tuono_lib/src/server.rs b/crates/tuono_lib/src/server.rs new file mode 100644 index 00000000..364b4596 --- /dev/null +++ b/crates/tuono_lib/src/server.rs @@ -0,0 +1,53 @@ +use crate::mode::{Mode, GLOBAL_MODE}; + +use crate::manifest::load_manifest; +use axum::routing::{get, Router}; +use ssr_rs::Ssr; +use tower_http::services::ServeDir; + +use crate::catch_all::catch_all; + +const DEV_PUBLIC_DIR: &str = "public"; +const PROD_PUBLIC_DIR: &str = "out/client"; + +pub struct Server { + router: Router, + mode: Mode, +} + +impl Server { + pub fn init(router: Router, mode: Mode) -> Server { + Ssr::create_platform(); + + GLOBAL_MODE.set(mode).unwrap(); + + if mode == Mode::Prod { + load_manifest() + } + + Server { router, mode } + } + + pub async fn start(&self) { + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + + if self.mode == Mode::Dev { + println!("\nDevelopment app ready at http://localhost:3000/"); + } else { + println!("\nProduction app ready at http://localhost:3000/"); + } + + let public_dir = if self.mode == Mode::Dev { + DEV_PUBLIC_DIR + } else { + PROD_PUBLIC_DIR + }; + + let router = self + .router + .to_owned() + .fallback_service(ServeDir::new(public_dir).fallback(get(catch_all))); + + axum::serve(listener, router).await.unwrap(); + } +} diff --git a/crates/tuono_lib/src/ssr.rs b/crates/tuono_lib/src/ssr.rs index d79cab16..81f2ef17 100644 --- a/crates/tuono_lib/src/ssr.rs +++ b/crates/tuono_lib/src/ssr.rs @@ -1,4 +1,4 @@ -use crate::{Mode, GLOBAL_MODE}; +use crate::mode::{Mode, GLOBAL_MODE}; use lazy_static::lazy_static; use ssr_rs::Ssr; use std::cell::RefCell; diff --git a/crates/tuono_lib_macros/Cargo.toml b/crates/tuono_lib_macros/Cargo.toml index 81675db3..385d935b 100644 --- a/crates/tuono_lib_macros/Cargo.toml +++ b/crates/tuono_lib_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tuono_lib_macros" -version = "0.3.0" +version = "0.3.1" edition = "2021" description = "The react/rust fullstack framework" repository = "https://github.com/Valerioageno/tuono" diff --git a/crates/tuono_lib_macros/src/handler.rs b/crates/tuono_lib_macros/src/handler.rs index 0c8f2bae..a25cd0f7 100644 --- a/crates/tuono_lib_macros/src/handler.rs +++ b/crates/tuono_lib_macros/src/handler.rs @@ -8,9 +8,9 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream { let fn_name = item.clone().sig.ident; quote! { - use axum::response::IntoResponse; + use tuono_lib::axum::response::IntoResponse; use std::collections::HashMap; - use axum::extract::{State, Path}; + use tuono_lib::axum::extract::{State, Path}; use reqwest::Client; #item @@ -18,7 +18,7 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream { pub async fn route( Path(params): Path>, State(client): State, - request: axum::extract::Request + request: tuono_lib::axum::extract::Request ) -> impl IntoResponse { let pathname = &request.uri(); let headers = &request.headers(); @@ -31,7 +31,7 @@ pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream { pub async fn api( Path(params): Path>, State(client): State, - request: axum::extract::Request + request: tuono_lib::axum::extract::Request ) -> impl IntoResponse{ let pathname = &request.uri(); let headers = &request.headers(); diff --git a/examples/tuono/Cargo.toml b/examples/tuono/Cargo.toml index 5ba88e75..5f576a76 100644 --- a/examples/tuono/Cargo.toml +++ b/examples/tuono/Cargo.toml @@ -8,11 +8,5 @@ name = "tuono" path = ".tuono/main.rs" [dependencies] -axum = {version = "0.7.5", features = ["json"]} -tokio = { version = "1.37.0", features = ["full"] } -tower-http = {version = "0.5.2", features = ["fs"]} -serde = { version = "1.0.202", features = ["derive"] } tuono_lib = { path = "../../crates/tuono_lib/"} -serde_json = "1.0" -reqwest = {version = "0.12.4", features = ["json"]} diff --git a/examples/tutorial/Cargo.toml b/examples/tutorial/Cargo.toml index 5ba88e75..aa22378f 100644 --- a/examples/tutorial/Cargo.toml +++ b/examples/tutorial/Cargo.toml @@ -8,11 +8,7 @@ name = "tuono" path = ".tuono/main.rs" [dependencies] -axum = {version = "0.7.5", features = ["json"]} -tokio = { version = "1.37.0", features = ["full"] } -tower-http = {version = "0.5.2", features = ["fs"]} -serde = { version = "1.0.202", features = ["derive"] } tuono_lib = { path = "../../crates/tuono_lib/"} -serde_json = "1.0" +serde = { version = "1.0.202", features = ["derive"] } reqwest = {version = "0.12.4", features = ["json"]} diff --git a/packages/lazy-fn-vite-plugin/package.json b/packages/lazy-fn-vite-plugin/package.json index 6da5d874..68e2b95f 100644 --- a/packages/lazy-fn-vite-plugin/package.json +++ b/packages/lazy-fn-vite-plugin/package.json @@ -1,6 +1,6 @@ { "name": "tuono-lazy-fn-vite-plugin", - "version": "0.3.0", + "version": "0.3.1", "description": "Plugin for the tuono's lazy fn. Tuono is the react/rust fullstack framework", "scripts": { "dev": "vite build --watch", diff --git a/packages/tuono/package.json b/packages/tuono/package.json index fe73c7a3..28a2f0aa 100644 --- a/packages/tuono/package.json +++ b/packages/tuono/package.json @@ -1,6 +1,6 @@ { "name": "tuono", - "version": "0.3.0", + "version": "0.3.1", "description": "The react/rust fullstack framework", "scripts": { "dev": "vite build --watch",