mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 14:54:16 +00:00
WIP Implementation of expanded LeptosOptions, which interop with the cargo-leptos beta and allow configuration of Leptos. Adds id option to <Stylesheet/>
This commit is contained in:
parent
80287f7a61
commit
d5fbeb9474
2 changed files with 99 additions and 46 deletions
|
@ -1,67 +1,61 @@
|
|||
pub mod errors;
|
||||
|
||||
use crate::errors::LeptosConfigError;
|
||||
use config::{Config, File, FileFormat};
|
||||
use regex::Regex;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fs;
|
||||
use std::{env::VarError, net::SocketAddr, str::FromStr};
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
/// This struct serves as a convenient place to store details used for rendering.
|
||||
/// It's serialized into a file in the root called `.leptos.kdl` for cargo-leptos
|
||||
/// to watch. It's also used in our actix and axum integrations to generate the
|
||||
/// correct path for WASM, JS, and Websockets. Its goal is to be the single source
|
||||
/// of truth for render options
|
||||
#[derive(TypedBuilder, Clone)]
|
||||
pub struct RenderOptions {
|
||||
/// The path and name of the WASM and JS files generated by wasm-bindgen
|
||||
/// For example, `/pkg/app` might be a valid input if your crate name was `app`.
|
||||
/// This struct serves as a convenient place to store details used for configuring Leptos.
|
||||
/// It's used in our actix and axum integrations to generate the
|
||||
/// correct path for WASM, JS, and Websockets, as well as other configuration tasks.
|
||||
/// It shares keys with cargo-leptos, to allow for easy interoperability
|
||||
#[derive(TypedBuilder, Clone, serde::Deserialize)]
|
||||
pub struct LeptosOptions {
|
||||
/// The path of the WASM and JS files generated by wasm-bindgen from the root of your app
|
||||
/// By default, wasm-bindgen puts them in `/pkg`.
|
||||
#[builder(setter(into))]
|
||||
pub pkg_path: String,
|
||||
/// Used to control whether the Websocket code for code watching is included.
|
||||
/// I recommend passing in the result of `env::var("RUST_ENV")`
|
||||
/// The name of the WASM and JS files generated by wasm-bindgen.
|
||||
#[builder(setter(into))]
|
||||
pub pkg_name: String,
|
||||
/// Used to configure the running environment of Leptos. Can be used to load dev constants and keys v prod, or change
|
||||
/// things based on the deployment environment
|
||||
/// I recommend passing in the result of `env::var("LEPTOS__ENV")`
|
||||
#[builder(setter(into), default)]
|
||||
pub environment: RustEnv,
|
||||
pub env: Env,
|
||||
/// Provides a way to control the address leptos is served from.
|
||||
/// Using an env variable here would allow you to run the same code in dev and prod
|
||||
/// Defaults to `127.0.0.1:3000`
|
||||
#[builder(setter(into), default=SocketAddr::from(([127,0,0,1], 3000)))]
|
||||
pub socket_address: SocketAddr,
|
||||
pub site_address: SocketAddr,
|
||||
/// The port the Websocket watcher listens on. Should match the `reload_port` in cargo-leptos(if using).
|
||||
/// Defaults to `3001`
|
||||
#[builder(default = 3001)]
|
||||
pub reload_port: u32,
|
||||
/// This controls whether the Leptos Websocket Autoreload JS is included for each page
|
||||
/// Defaults to false
|
||||
#[builder(default = false)]
|
||||
pub leptos_watch: bool,
|
||||
}
|
||||
|
||||
impl RenderOptions {
|
||||
/// Creates a hidden file at ./.leptos_toml so cargo-leptos can monitor settings. We do not read from this file
|
||||
/// only write to it, you'll want to change the settings in your main function when you create RenderOptions
|
||||
pub fn write_to_file(&self) {
|
||||
use std::fs;
|
||||
let options = format!(
|
||||
r#"// This file is auto-generated. Changing it will have no effect on leptos. Change these by changing RenderOptions and rerunning
|
||||
RenderOptions {{
|
||||
pkg-path "{}"
|
||||
environment "{:?}"
|
||||
socket-address "{:?}"
|
||||
reload-port {:?}
|
||||
}}
|
||||
"#,
|
||||
self.pkg_path, self.environment, self.socket_address, self.reload_port
|
||||
);
|
||||
fs::write("./.leptos.kdl", options).expect("Unable to write file");
|
||||
}
|
||||
}
|
||||
/// An enum that can be used to define the environment Leptos is running in. Can be passed to RenderOptions.
|
||||
/// Setting this to the PROD variant will not include the websockets code for cargo-leptos' watch.
|
||||
/// Defaults to PROD
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RustEnv {
|
||||
/// Defaults to DEV
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
pub enum Env {
|
||||
PROD,
|
||||
DEV,
|
||||
}
|
||||
|
||||
impl Default for RustEnv {
|
||||
impl Default for Env {
|
||||
fn default() -> Self {
|
||||
Self::PROD
|
||||
Self::DEV
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RustEnv {
|
||||
impl FromStr for Env {
|
||||
type Err = ();
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let sanitized = input.to_lowercase();
|
||||
|
@ -70,12 +64,12 @@ impl FromStr for RustEnv {
|
|||
"development" => Ok(Self::DEV),
|
||||
"prod" => Ok(Self::PROD),
|
||||
"production" => Ok(Self::PROD),
|
||||
_ => Ok(Self::PROD),
|
||||
_ => Ok(Self::DEV),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RustEnv {
|
||||
impl From<&str> for Env {
|
||||
fn from(str: &str) -> Self {
|
||||
let sanitized = str.to_lowercase();
|
||||
match sanitized.as_str() {
|
||||
|
@ -84,12 +78,12 @@ impl From<&str> for RustEnv {
|
|||
"prod" => Self::PROD,
|
||||
"production" => Self::PROD,
|
||||
_ => {
|
||||
panic!("Environment var is not recognized. Maybe try `dev` or `prod`")
|
||||
panic!("Env var is not recognized. Maybe try `dev` or `prod`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<&Result<String, VarError>> for RustEnv {
|
||||
impl From<&Result<String, VarError>> for Env {
|
||||
fn from(input: &Result<String, VarError>) -> Self {
|
||||
match input {
|
||||
Ok(str) => {
|
||||
|
@ -100,11 +94,64 @@ impl From<&Result<String, VarError>> for RustEnv {
|
|||
"prod" => Self::PROD,
|
||||
"production" => Self::PROD,
|
||||
_ => {
|
||||
panic!("Environment var is not recognized. Maybe try `dev` or `prod`")
|
||||
panic!("Env var is not recognized. Maybe try `dev` or `prod`")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => Self::PROD,
|
||||
Err(_) => Self::DEV,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Env {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"dev" => Ok(Self::DEV),
|
||||
"development" => Ok(Self::DEV),
|
||||
"prod" => Ok(Self::PROD),
|
||||
"production" => Ok(Self::PROD),
|
||||
other => Err(format!(
|
||||
"{} is not a supported environment. Use either `dev` or `production`.",
|
||||
other
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_configuration() -> Result<LeptosOptions, LeptosConfigError> {
|
||||
let text = fs::read_to_string("Cargo.toml").map_err(|_| LeptosConfigError::ConfigNotFound)?;
|
||||
let re: Regex = Regex::new(r#"(?m)^\[package.metadata.leptos\]"#).unwrap();
|
||||
let start = match re.find(&text) {
|
||||
Some(found) => found.start(),
|
||||
None => return Err(LeptosConfigError::ConfigSectionNotFound),
|
||||
};
|
||||
println!("Config file content:\n{text}");
|
||||
|
||||
// so that serde error messages have right line number
|
||||
let newlines = text[..start].matches('\n').count();
|
||||
let toml = "\n".repeat(newlines) + &text[start..];
|
||||
|
||||
// Detect the running environment.
|
||||
// Default to `local` if unspecified.
|
||||
let environment: Env = std::env::var("LEPTOS_ENV")
|
||||
.unwrap_or_else(|_| "dev".into())
|
||||
.try_into()
|
||||
.map_err(|_| LeptosConfigError::EnvError)?; //shouldn't happen
|
||||
|
||||
println!("Loading configuration for environment: {:?}", environment);
|
||||
|
||||
let settings = Config::builder()
|
||||
// Read the "default" configuration file
|
||||
.add_source(File::from_str(&toml, FileFormat::Toml))
|
||||
// Layer on the environment-specific values.
|
||||
// Add in settings from environment variables (with a prefix of APP and '__' as separator)
|
||||
// E.g. `APP_APPLICATION__PORT=5001 would set `Settings.application.port`
|
||||
.add_source(config::Environment::with_prefix("LEPTOS").separator("__"))
|
||||
.build()?;
|
||||
|
||||
settings
|
||||
.try_deserialize()
|
||||
.map_err(|e| LeptosConfigError::ConfigError(e.to_string()))
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ pub struct StylesheetProps {
|
|||
/// The URL at which the stylesheet can be located.
|
||||
#[builder(setter(into))]
|
||||
pub href: String,
|
||||
/// The URL at which the stylesheet can be located.
|
||||
#[builder(setter(into, strip_option))]
|
||||
pub id: Option<String>,
|
||||
}
|
||||
|
||||
/// Injects an [HTMLLinkElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement) into the document
|
||||
|
@ -49,7 +52,7 @@ pub struct StylesheetProps {
|
|||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Stylesheet(cx: Scope, props: StylesheetProps) {
|
||||
let StylesheetProps { href } = props;
|
||||
let StylesheetProps { href, id } = props;
|
||||
cfg_if! {
|
||||
if #[cfg(any(feature = "csr", feature = "hydrate"))] {
|
||||
use leptos::document;
|
||||
|
@ -66,6 +69,9 @@ pub fn Stylesheet(cx: Scope, props: StylesheetProps) {
|
|||
} else {
|
||||
let el = document().create_element("link").unwrap_throw();
|
||||
el.set_attribute("rel", "stylesheet").unwrap_throw();
|
||||
if let Some(id) = id{
|
||||
el.set_attribute("id", &id).unwrap_throw();
|
||||
}
|
||||
el.set_attribute("href", &href).unwrap_throw();
|
||||
document()
|
||||
.query_selector("head")
|
||||
|
|
Loading…
Reference in a new issue