mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Commit WIP version of common config struct that writes a KDL file for cargo-leptos
This commit is contained in:
parent
7b376b6d3a
commit
f63cb02277
13 changed files with 158 additions and 29 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ blob.rs
|
|||
Cargo.lock
|
||||
**/*.rs.bk
|
||||
.DS_Store
|
||||
.leptos.kdl
|
||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
|||
"leptos",
|
||||
"leptos_dom",
|
||||
"leptos_core",
|
||||
"leptos_config",
|
||||
"leptos_macro",
|
||||
"leptos_reactive",
|
||||
"leptos_server",
|
||||
|
|
|
@ -12,6 +12,7 @@ if #[cfg(feature = "ssr")] {
|
|||
use http::StatusCode;
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::services::ServeDir;
|
||||
use leptos_axum::RenderOptions;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
@ -36,12 +37,14 @@ if #[cfg(feature = "ssr")] {
|
|||
)
|
||||
}
|
||||
|
||||
let render_options: RenderOptions = leptos_axum::RenderOptionsBuilder::default().client_pkg_path("/pkg/leptos_hackernews_axum").auto_reload(3001).build().expect("Failed to Parse RenderOptions");
|
||||
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
// `GET /` goes to `root`
|
||||
.nest_service("/pkg", pkg_service)
|
||||
.nest_service("/static", static_service)
|
||||
.fallback(leptos_axum::render_app_to_stream("/pkg/leptos_hackernews_axum", |cx| view! { cx, <App/> }));
|
||||
.fallback(leptos_axum::render_app_to_stream(render_options, |cx| view! { cx, <App/> }));
|
||||
|
||||
// run our app with hyper
|
||||
// `axum::Server` is a re-export of `hyper::Server`
|
||||
|
|
|
@ -14,7 +14,7 @@ if #[cfg(feature = "ssr")] {
|
|||
use todo_app_sqlite_axum::*;
|
||||
use http::StatusCode;
|
||||
use tower_http::services::ServeDir;
|
||||
use leptos_axum::RenderOptions;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
@ -45,8 +45,9 @@ if #[cfg(feature = "ssr")] {
|
|||
)
|
||||
}
|
||||
|
||||
let render_options: RenderOptions = leptos_axum::RenderOptionsBuilder::default().client_pkg_path("/pkg/todo_app_sqlite_axum").auto_reload(3001).build().expect("Failed to Parse RenderOptions");
|
||||
|
||||
let render_options: RenderOptions = RenderOptions::builder().pkg_path("/pkg/todo_app_sqlite_axum").reload_port(3001).environment(&env::var("RUST_ENV")).build();
|
||||
render_options.write_to_file();
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
||||
|
|
|
@ -12,9 +12,9 @@ cfg_if! {
|
|||
}
|
||||
|
||||
pub fn register_server_functions() {
|
||||
GetTodos::register();
|
||||
AddTodo::register();
|
||||
DeleteTodo::register();
|
||||
_ = GetTodos::register();
|
||||
_ = AddTodo::register();
|
||||
_ = DeleteTodo::register();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
|
@ -34,7 +34,7 @@ cfg_if! {
|
|||
}
|
||||
|
||||
#[server(GetTodos, "/api")]
|
||||
pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
||||
pub async fn get_todos(_cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
||||
// this is just an example of how to access server context injected in the handlers
|
||||
// http::Request doesn't implement Clone, so more work will be needed to do use_context() on this
|
||||
// let req = use_context::<http::Request<axum::body::BoxBody>>(cx)
|
||||
|
@ -70,7 +70,7 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
|
|||
.execute(&mut conn)
|
||||
.await
|
||||
{
|
||||
Ok(row) => Ok(()),
|
||||
Ok(_row) => Ok(()),
|
||||
Err(e) => Err(ServerFnError::ServerError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ pub fn Todos(cx: Scope) -> Element {
|
|||
<li>
|
||||
{todo.title}
|
||||
<ActionForm action=delete_todo.clone()>
|
||||
<input type="hidden" name="id" value={todo.id}/>
|
||||
<input type="hidden" name="id" value=todo.id/>
|
||||
<input type="submit" value="X"/>
|
||||
</ActionForm>
|
||||
</li>
|
||||
|
|
|
@ -9,6 +9,7 @@ cfg_if! {
|
|||
use actix_files::{Files};
|
||||
use actix_web::*;
|
||||
use crate::todo::*;
|
||||
use std::env;
|
||||
|
||||
#[get("/style.css")]
|
||||
async fn css() -> impl Responder {
|
||||
|
@ -26,11 +27,14 @@ cfg_if! {
|
|||
crate::todo::register_server_functions();
|
||||
|
||||
HttpServer::new(|| {
|
||||
let render_options: RenderOptions = RenderOptions::builder().pkg_path("/pkg/todo_app_sqlite").reload_port(3001).environment(&env::var("RUST_ENV")).build();
|
||||
render_options.write_to_file();
|
||||
|
||||
App::new()
|
||||
.service(Files::new("/pkg", "./pkg"))
|
||||
.service(css)
|
||||
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
|
||||
.route("/{tail:.*}", leptos_actix::render_app_to_stream("/pkg/todo_app_sqlite", |cx| view! { cx, <TodoApp/> }))
|
||||
.route("/{tail:.*}", leptos_actix::render_app_to_stream(render_options, |cx| view! { cx, <TodoApp/> }))
|
||||
//.wrap(middleware::Compress::default())
|
||||
})
|
||||
.bind(("127.0.0.1", 8083))?
|
||||
|
|
|
@ -138,10 +138,11 @@ pub fn handle_server_fns() -> Route {
|
|||
/// # }
|
||||
/// ```
|
||||
pub fn render_app_to_stream(
|
||||
client_pkg_path: &'static str,
|
||||
options: RenderOptions,
|
||||
app_fn: impl Fn(leptos::Scope) -> Element + Clone + 'static,
|
||||
) -> Route {
|
||||
web::get().to(move |req: HttpRequest| {
|
||||
let options = options.clone();
|
||||
let app_fn = app_fn.clone();
|
||||
async move {
|
||||
let path = req.path();
|
||||
|
@ -165,12 +166,39 @@ pub fn render_app_to_stream(
|
|||
}
|
||||
};
|
||||
|
||||
let head = format!(r#"<!DOCTYPE html>
|
||||
<html>
|
||||
let pkg_path = &options.pkg_path;
|
||||
|
||||
let leptos_autoreload = match options.reload_port {
|
||||
Some(port) => match &options.environment {
|
||||
RustEnv::DEV => format!(
|
||||
r#"
|
||||
<script crossorigin="">(function () {{
|
||||
var ws = new WebSocket('ws://127.0.0.1:{port}/autoreload');
|
||||
ws.onmessage = (ev) => {{
|
||||
console.log(`Reload message: `);
|
||||
if (ev.data === 'reload') window.location.reload();
|
||||
}};
|
||||
ws.onclose = () => console.warn('Autoreload stopped. Manual reload necessary.');
|
||||
}})()
|
||||
</script>
|
||||
"#
|
||||
),
|
||||
RustEnv::PROD => "".to_string(),
|
||||
},
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
let head = format!(
|
||||
r#"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<script type="module">import init, {{ hydrate }} from '{client_pkg_path}.js'; init().then(hydrate);</script>"#);
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init().then(hydrate);</script>
|
||||
{leptos_autoreload}
|
||||
"#
|
||||
);
|
||||
|
||||
let tail = "</body></html>";
|
||||
|
||||
HttpResponse::Ok().content_type("text/html").streaming(
|
||||
|
|
|
@ -11,6 +11,7 @@ description = "Axum integrations for the Leptos web framework."
|
|||
axum = "0.6"
|
||||
derive_builder = "0.12.0"
|
||||
futures = "0.3"
|
||||
kdl = "4.6.0"
|
||||
leptos = { path = "../../leptos", default-features = false, version = "0.0", features = [
|
||||
"ssr",
|
||||
] }
|
||||
|
|
|
@ -4,7 +4,6 @@ use axum::{
|
|||
http::{HeaderMap, HeaderValue, Request, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use derive_builder::Builder;
|
||||
use futures::{Future, SinkExt, Stream, StreamExt};
|
||||
use leptos::*;
|
||||
use leptos_meta::MetaContext;
|
||||
|
@ -128,14 +127,6 @@ pub async fn handle_server_fns(
|
|||
|
||||
pub type PinnedHtmlStream = Pin<Box<dyn Stream<Item = io::Result<Bytes>> + Send>>;
|
||||
|
||||
#[derive(Default, Builder, Clone)]
|
||||
pub struct RenderOptions {
|
||||
#[builder(setter(into))]
|
||||
client_pkg_path: String,
|
||||
#[builder(setter(strip_option), default)]
|
||||
auto_reload: Option<u32>,
|
||||
}
|
||||
|
||||
/// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries
|
||||
/// to route it using [leptos_router], serving an HTML stream of your application.
|
||||
///
|
||||
|
@ -201,11 +192,12 @@ pub fn render_app_to_stream(
|
|||
full_path = "http://leptos".to_string() + &path.to_string()
|
||||
}
|
||||
|
||||
let client_pkg_path = &options.client_pkg_path;
|
||||
let pkg_path = &options.pkg_path;
|
||||
|
||||
let leptos_autoreload = match options.auto_reload {
|
||||
Some(port) => format!(
|
||||
r#"
|
||||
let leptos_autoreload = match options.reload_port {
|
||||
Some(port) => match &options.environment {
|
||||
RustEnv::DEV => format!(
|
||||
r#"
|
||||
<script crossorigin="">(function () {{
|
||||
var ws = new WebSocket('ws://127.0.0.1:{port}/autoreload');
|
||||
ws.onmessage = (ev) => {{
|
||||
|
@ -216,7 +208,9 @@ pub fn render_app_to_stream(
|
|||
}})()
|
||||
</script>
|
||||
"#
|
||||
),
|
||||
),
|
||||
RustEnv::PROD => "".to_string(),
|
||||
},
|
||||
None => "".to_string(),
|
||||
};
|
||||
|
||||
|
@ -226,7 +220,7 @@ pub fn render_app_to_stream(
|
|||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<script type="module">import init, {{ hydrate }} from '{client_pkg_path}.js'; init().then(hydrate);</script>
|
||||
<script type="module">import init, {{ hydrate }} from '{pkg_path}.js'; init().then(hydrate);</script>
|
||||
{leptos_autoreload}
|
||||
"#
|
||||
);
|
||||
|
|
|
@ -10,6 +10,7 @@ readme = "../README.md"
|
|||
|
||||
[dependencies]
|
||||
leptos_core = { path = "../leptos_core", default-features = false, version = "0.0.19" }
|
||||
leptos_config = { path = "../leptos_config", default-features = false, version = "0.0.1" }
|
||||
leptos_dom = { path = "../leptos_dom", default-features = false, version = "0.0.19" }
|
||||
leptos_macro = { path = "../leptos_macro", default-features = false, version = "0.0.19" }
|
||||
leptos_reactive = { path = "../leptos_reactive", default-features = false, version = "0.0.19" }
|
||||
|
|
|
@ -141,6 +141,7 @@
|
|||
//! # }
|
||||
//! ```
|
||||
|
||||
pub use leptos_config::*;
|
||||
pub use leptos_core::*;
|
||||
pub use leptos_dom;
|
||||
pub use leptos_dom::wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
|
|
11
leptos_config/Cargo.toml
Normal file
11
leptos_config/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "leptos_config"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
authors = ["Greg Johnston"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/gbj/leptos"
|
||||
description = "Configuraiton for Leptos"
|
||||
|
||||
[dependencies]
|
||||
typed-builder = "0.11.0"
|
83
leptos_config/src/lib.rs
Normal file
83
leptos_config/src/lib.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use std::{env::VarError, str::FromStr};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
#[derive(Default, TypedBuilder, Clone)]
|
||||
pub struct RenderOptions {
|
||||
#[builder(setter(into))]
|
||||
pub pkg_path: String,
|
||||
#[builder(setter(into))]
|
||||
pub environment: RustEnv,
|
||||
#[builder(setter(strip_option), default)]
|
||||
pub reload_port: Option<u32>,
|
||||
}
|
||||
|
||||
impl RenderOptions {
|
||||
/// Creates a hidden file at ./.leptos_toml so cargo-leptos can monitor settings
|
||||
pub fn write_to_file(&self) {
|
||||
use std::fs;
|
||||
let options = format!(
|
||||
r#"render_options: {{
|
||||
pkg_path {}
|
||||
environment {:?}
|
||||
reload_port {:?}
|
||||
}}
|
||||
"#,
|
||||
self.pkg_path, self.environment, self.reload_port
|
||||
);
|
||||
fs::write("./.leptos.kdl", options).expect("Unable to write file");
|
||||
}
|
||||
}
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub enum RustEnv {
|
||||
#[default]
|
||||
PROD,
|
||||
DEV,
|
||||
}
|
||||
|
||||
impl FromStr for RustEnv {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let sanitized = input.to_lowercase();
|
||||
match sanitized.as_ref() {
|
||||
"dev" => Ok(Self::DEV),
|
||||
"development" => Ok(Self::DEV),
|
||||
"prod" => Ok(Self::PROD),
|
||||
"production" => Ok(Self::PROD),
|
||||
_ => Ok(Self::PROD),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RustEnv {
|
||||
fn from(str: &str) -> Self {
|
||||
let sanitized = str.to_lowercase();
|
||||
match sanitized.as_str() {
|
||||
"dev" => Self::DEV,
|
||||
"development" => Self::DEV,
|
||||
"prod" => Self::PROD,
|
||||
"production" => Self::PROD,
|
||||
_ => {
|
||||
panic!("Environment var is not recognized. Maybe try `dev` or `prod`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<&Result<String, VarError>> for RustEnv {
|
||||
fn from(input: &Result<String, VarError>) -> Self {
|
||||
match input {
|
||||
Ok(str) => {
|
||||
let sanitized = str.to_lowercase();
|
||||
match sanitized.as_ref() {
|
||||
"dev" => Self::DEV,
|
||||
"development" => Self::DEV,
|
||||
"prod" => Self::PROD,
|
||||
"production" => Self::PROD,
|
||||
_ => {
|
||||
panic!("Environment var is not recognized. Maybe try `dev` or `prod`")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => Self::PROD,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue