diff --git a/Cargo.toml b/Cargo.toml index a4161eab..2fdb2cb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,9 @@ resolver = "2" members = [ "crates/cli", "crates/axum_bundler", - "crates/bundler" + "crates/bundler", + "crates/tuono_lib", + "crates/tuono_lib_macros" ] exclude = [ "examples/playground" diff --git a/README.md b/README.md index ab2fb5e2..1da8f89c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ Why Tuono? Just a badass name. cargo install tuono ``` +## Create a new project + +``` +tuono new +``` + ## Development ``` diff --git a/crates/axum_bundler/src/bundle.rs b/crates/axum_bundler/src/bundle.rs index 9cceb54e..d6bcf52d 100644 --- a/crates/axum_bundler/src/bundle.rs +++ b/crates/axum_bundler/src/bundle.rs @@ -5,25 +5,18 @@ use std::io::prelude::*; use std::path::Path; use std::path::PathBuf; +const ROOT_FOLDER: &'static str = "src/routes"; + const AXUM_ENTRY_POINT: &'static str = r##" // File automatically generated // Do not manually change it use axum::response::Html; use axum::{http::Uri, routing::get, Router}; -use ssr_rs::Ssr; -use std::cell::RefCell; -use std::fs::read_to_string; use tower_http::services::ServeDir; +use tuono_lib::{ssr, Ssr}; -thread_local! { - static SSR: RefCell> = RefCell::new( - Ssr::from( - read_to_string("./out/server/server-main.js").unwrap(), - "" - ).unwrap() - ) -} +// MODULE_IMPORTS #[tokio::main] async fn main() { @@ -48,18 +41,13 @@ async fn catch_all(uri: Uri) -> Html { }}"# ); - let result = SSR.with(|ssr| ssr.borrow_mut().render_to_string(Some(&payload))); + 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()), } } - - -// ROUTE_DECLARATIONS - - "##; fn create_main_file(base_path: &Path, bundled_file: &String) { @@ -71,8 +59,6 @@ fn create_main_file(base_path: &Path, bundled_file: &String) { .expect("write failed"); } -fn clean_up_source_file(file: &mut String) {} - fn get_route_name<'a>(path: &PathBuf, base_path: &Path) -> String { let base_path_str = base_path.to_str().unwrap(); if base_path_str == "." { @@ -96,9 +82,9 @@ fn get_route_name<'a>(path: &PathBuf, base_path: &Path) -> String { .replace("index", "") } -fn collect_handlers(base_path: &Path) -> (String, HashMap) { - let mut declarations = String::from("// ROUTE_DECLARATIONS\n"); - let mut fn_map: HashMap = HashMap::new(); +fn collect_handlers(base_path: &Path) -> HashMap { + // + let mut mods_map: HashMap = HashMap::new(); let _: Vec<_> = glob(base_path.join("src/routes/**/*.rs").to_str().unwrap()) .unwrap() @@ -106,29 +92,45 @@ fn collect_handlers(base_path: &Path) -> (String, HashMap) { let entry = entry.unwrap(); let route_name = get_route_name(&entry, base_path); - let mut file_content = fs::read_to_string(entry).unwrap(); - clean_up_source_file(&mut file_content); - let hash = fastmurmur3::hash(file_content.as_bytes()); - let new_fn_name = format!("fn_{}", hash); - let file_content = file_content.replace("get_server_side_props", &new_fn_name); + // Remove first slash + let mut module = route_name.as_str().chars(); + module.next(); - fn_map.insert(route_name, new_fn_name); - - declarations.push_str(&file_content); + mods_map.insert( + module.as_str().to_string(), + entry + .to_str() + .unwrap() + .replace(base_path.to_str().unwrap(), ""), + ); }) .collect(); - println!("{fn_map:?}"); + dbg!(&mods_map); - return (declarations, fn_map); + return mods_map; } -fn create_axum_routes_declaration(routes: HashMap) -> String { +fn create_axum_routes_declaration(routes: &HashMap) -> String { let mut route_declarations = String::from("// ROUTE_BUILDER\n"); - for (route, handler) in routes.iter() { - route_declarations.push_str(&format!(r#".route("{route}", get({handler}))"#)) + for (route, _path) in routes.iter() { + route_declarations.push_str(&format!(r#".route("/{route}", get({route}::route))"#)) + } + + route_declarations +} + +fn create_modules_declaration(routes: &HashMap) -> String { + let mut route_declarations = String::from("// MODULE_IMPORTS\n"); + + for (route, path) in routes.iter() { + route_declarations.push_str(&format!( + r#"#[path="..{path}"] +mod {route}; +"# + )) } route_declarations @@ -139,14 +141,11 @@ pub fn bundle() { let base_path = std::env::current_dir().unwrap(); - let (handlers, fn_map) = collect_handlers(&base_path); + let mods = collect_handlers(&base_path); - let bundled_file = AXUM_ENTRY_POINT.replace( - "// ROUTE_BUILDER\n", - &create_axum_routes_declaration(fn_map), - ); - - let bundled_file = bundled_file.replace("// ROUTE_DECLARATIONS", &handlers[..]); + let bundled_file = AXUM_ENTRY_POINT + .replace("// ROUTE_BUILDER\n", &create_axum_routes_declaration(&mods)) + .replace("// MODULE_IMPORTS\n", &create_modules_declaration(&mods)); create_main_file(&base_path, &bundled_file); } diff --git a/crates/bundler/src/bundler.rs b/crates/bundler/src/bundler.rs index 5089986c..53efac78 100644 --- a/crates/bundler/src/bundler.rs +++ b/crates/bundler/src/bundler.rs @@ -9,6 +9,7 @@ use watchexec_supervisor::job::start_job; #[tokio::main] pub async fn watch() -> Result<()> { + bundle(); let (job, _) = start_job(Arc::new(Command { program: Program::Exec { prog: "cargo".into(), diff --git a/crates/tuono_lib/Cargo.toml b/crates/tuono_lib/Cargo.toml new file mode 100644 index 00000000..d7efe25a --- /dev/null +++ b/crates/tuono_lib/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tuono_lib" +version = "0.0.1" +edition = "2021" +authors = ["V. Ageno "] + +[lib] +name = "tuono_lib" +path = "src/lib.rs" + +[dependencies] +ssr_rs = "0.5.1" +tuono_lib_macros = { path = "../tuono_lib_macros"} diff --git a/crates/tuono_lib/README.md b/crates/tuono_lib/README.md new file mode 100644 index 00000000..c4eb7228 --- /dev/null +++ b/crates/tuono_lib/README.md @@ -0,0 +1,3 @@ +# Tuonolib + +This project exposes the interfaces to easily handle the backend build. diff --git a/crates/tuono_lib/src/lib.rs b/crates/tuono_lib/src/lib.rs new file mode 100644 index 00000000..f2694b25 --- /dev/null +++ b/crates/tuono_lib/src/lib.rs @@ -0,0 +1,8 @@ +pub mod ssr; +pub use ssr_rs::Ssr; +pub use tuono_lib_macros::handler; + +pub enum Response { + Redirect(String), + Props(String), +} diff --git a/crates/tuono_lib/src/ssr.rs b/crates/tuono_lib/src/ssr.rs new file mode 100644 index 00000000..5f83daa0 --- /dev/null +++ b/crates/tuono_lib/src/ssr.rs @@ -0,0 +1,15 @@ +use ssr_rs::Ssr; +use std::cell::RefCell; +use std::fs::read_to_string; + +pub struct Js; +impl Js { + thread_local! { + pub static SSR: RefCell> = RefCell::new( + Ssr::from( + read_to_string("./out/server/server-main.js").unwrap(), + "" + ).unwrap() + ) + } +} diff --git a/crates/tuono_lib_macros/Cargo.toml b/crates/tuono_lib_macros/Cargo.toml new file mode 100644 index 00000000..e4539ee4 --- /dev/null +++ b/crates/tuono_lib_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tuono_lib_macros" +version = "0.0.1" +edition = "2021" + +[lib] +crate_type = ["proc-macro"] +proc-marco = true + +[dependencies] +syn = { version = "1.0.59", features = ["full"] } +quote = "1.0" diff --git a/crates/tuono_lib_macros/README.md b/crates/tuono_lib_macros/README.md new file mode 100644 index 00000000..28fc1977 --- /dev/null +++ b/crates/tuono_lib_macros/README.md @@ -0,0 +1 @@ +# Tuonolib Macro diff --git a/crates/tuono_lib_macros/src/handler.rs b/crates/tuono_lib_macros/src/handler.rs new file mode 100644 index 00000000..04a51a3d --- /dev/null +++ b/crates/tuono_lib_macros/src/handler.rs @@ -0,0 +1,35 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemFn}; + +pub fn handler_core(_args: TokenStream, item: TokenStream) -> TokenStream { + let item = parse_macro_input!(item as ItemFn); + + let fn_name = item.clone().sig.ident; + + quote! { + use tuono_lib::Response; + use axum::response::Html; + use axum::http::Uri; + use tuono_lib::ssr; + + #item + + pub async fn route(uri: Uri) -> Html { + println!("Custom handler"); + let props = #fn_name(); + + let res = match props { + Response::Props(val) => ssr::Js::SSR.with(|ssr| ssr.borrow_mut().render_to_string(Some(val.as_str()))), + _ => Ok("500 Internal server error".to_string()) + }; + + match res { + Ok(html) => Html(html), + _ => Html("500".to_string()) + } + + } + } + .into() +} diff --git a/crates/tuono_lib_macros/src/lib.rs b/crates/tuono_lib_macros/src/lib.rs new file mode 100644 index 00000000..70508078 --- /dev/null +++ b/crates/tuono_lib_macros/src/lib.rs @@ -0,0 +1,11 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +mod handler; + +#[proc_macro_attribute] +pub fn handler(args: TokenStream, item: TokenStream) -> TokenStream { + handler::handler_core(args, item) +} + +