Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix base path on web and fullstack #3247

Merged
merged 52 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
3fb6d4f
Create a crate for constant serialization of config structs for manganis
ealmloff Nov 11, 2024
349acab
use SerializeConst for the image asset builder
ealmloff Nov 11, 2024
3fe59ed
switch to a declarative macro for assets
ealmloff Nov 11, 2024
4db9c9a
clean up asset macro a bit
ealmloff Nov 11, 2024
b6533d0
add serializable options for each asset type
ealmloff Nov 14, 2024
d6c23b4
serialize const vec
ealmloff Nov 14, 2024
a346486
Add unique path formatting
ealmloff Nov 14, 2024
96043f1
implement the new manganis macro
ealmloff Nov 16, 2024
123b429
Merge branch 'main' into restore-manganis
ealmloff Nov 16, 2024
5faf8d5
optimize assets in the CLI
ealmloff Nov 18, 2024
a6aebfa
Fix clippy
ealmloff Nov 18, 2024
2c2c941
Fix assets with dioxus formattable
ealmloff Nov 18, 2024
f0e57c2
reduce fuzzing test limits
ealmloff Nov 18, 2024
32460a4
use the new syntax in examples
ealmloff Nov 18, 2024
7d92da5
fix formatting
ealmloff Nov 18, 2024
3904749
Final doc and test pass on const-serialize
ealmloff Nov 18, 2024
cc40767
fix avif support
ealmloff Nov 18, 2024
2d43173
Fix manganis doc tests
ealmloff Nov 18, 2024
000d143
cache asset optimization
ealmloff Nov 18, 2024
fa304a3
Split out asset and bundled asset
ealmloff Nov 18, 2024
713954d
Merge branch 'main' into restore-manganis
ealmloff Nov 18, 2024
0a8726f
make hash pointer width independent
ealmloff Nov 18, 2024
bb3ad52
remove const serialize cargo lock
ealmloff Nov 18, 2024
0502863
Fix manganis macro docs
ealmloff Nov 18, 2024
df38d3d
all tests passing
ealmloff Nov 18, 2024
712e9e3
add constvec::is_empty method to fix clippy lint
ealmloff Nov 18, 2024
59438c6
remove nasm feature
ealmloff Nov 18, 2024
8b22f6b
simplify test_rsplit_once test so typos passes
ealmloff Nov 18, 2024
50d0d41
fix range syntax for stable
ealmloff Nov 18, 2024
06985f7
revert example change from clippy fix
ealmloff Nov 18, 2024
67eaa4b
remove dioxus-static-site-generation workspace dependency
ealmloff Nov 18, 2024
0eaca01
always accept unix paths
ealmloff Nov 18, 2024
fb46b91
fix windows path seperator
ealmloff Nov 20, 2024
280b4de
fix folder asset hash
ealmloff Nov 20, 2024
c5b60a7
Optimize assets in a blocking task
ealmloff Nov 20, 2024
a7f7ed8
Fix asset options docs
ealmloff Nov 20, 2024
7d0dba1
Document Asset and BundledAsset
ealmloff Nov 20, 2024
d5b0809
move manganis back into it's own folder
ealmloff Nov 20, 2024
da5ee69
simplify the linker macro a bit
ealmloff Nov 20, 2024
14e5e53
add more docs to AssetParser expansion
ealmloff Nov 20, 2024
812a37c
fix manganis core doc test
ealmloff Nov 20, 2024
46c1312
add image format helpers
ealmloff Nov 20, 2024
d8b370c
Fill in new cargo.tomls
ealmloff Nov 20, 2024
627fb07
fix folders with explicit options
ealmloff Nov 21, 2024
9889aa0
Split by both unix and windows path separators and take the smallest…
ealmloff Nov 21, 2024
1e70dbc
fix string length
ealmloff Nov 21, 2024
b15341d
Fix base path on web and fullstack
ealmloff Nov 21, 2024
4415106
Merge branch 'restore-manganis' into fix-base-path-again
ealmloff Nov 23, 2024
695e21d
Fix assets with base path
ealmloff Nov 23, 2024
6708928
fix fullstack base route with trailing slash
ealmloff Nov 23, 2024
21de2d4
fix clippy
ealmloff Nov 23, 2024
76d1c71
Merge branch 'main' into fix-base-path-again
ealmloff Nov 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/cli-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ description = "CLI Configuration for dioxus-cli"
keywords = ["dom", "ui", "gui", "react", ]

[dependencies]
wasm-bindgen = { workspace = true, optional = true }

[features]
web = ["dep:wasm-bindgen"]
76 changes: 73 additions & 3 deletions packages/cli-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,29 @@ pub const ASSET_ROOT_ENV: &str = "DIOXUS_ASSET_ROOT";
pub const APP_TITLE_ENV: &str = "DIOXUS_APP_TITLE";
pub const OUT_DIR: &str = "DIOXUS_OUT_DIR";

/// Reads an environment variable at runtime in debug mode or at compile time in
/// release mode. When bundling in release mode, we will not be running under the
/// environment variables that the CLI sets, so we need to read them at compile time.
macro_rules! read_env_config {
($name:expr) => {{
#[cfg(debug_assertions)]
{
// In debug mode, read the environment variable set by the CLI at runtime
std::env::var($name).ok()
}

#[cfg(not(debug_assertions))]
{
// In release mode, read the environment variable set by the CLI at compile time
// This means the value will still be available when running the application
// standalone.
// We don't always read the environment variable at compile time to avoid rebuilding
// this crate when the environment variable changes.
option_env!($name).map(ToString::to_string)
}
}};
}

/// Get the address of the devserver for use over a raw socket
///
/// This is not a websocket! There's no protocol!
Expand Down Expand Up @@ -51,7 +74,7 @@ pub fn fullstack_address_or_localhost() -> SocketAddr {
}

pub fn app_title() -> Option<String> {
std::env::var(APP_TITLE_ENV).ok()
read_env_config!("DIOXUS_APP_TITLE")
}

pub fn always_on_top() -> Option<bool> {
Expand All @@ -64,8 +87,55 @@ pub fn is_cli_enabled() -> bool {
std::env::var(CLI_ENABLED_ENV).is_ok()
}

pub fn base_path() -> Option<PathBuf> {
std::env::var("DIOXUS_ASSET_ROOT").ok().map(PathBuf::from)
#[cfg(feature = "web")]
#[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
export function getMetaContents(meta_name) {
const selector = document.querySelector(`meta[name="${meta_name}"]`);
if (!selector) {
return null;
}
return selector.content;
}
"#)]
extern "C" {
#[wasm_bindgen(js_name = getMetaContents)]
pub fn get_meta_contents(selector: &str) -> Option<String>;
}

/// Get the path where the application will be served from. This is used by the router to format the URLs.
pub fn base_path() -> Option<String> {
// This may trigger when compiling to the server if you depend on another crate that pulls in
// the web feature. It might be better for the renderers to provide the current platform
// as a global context
#[cfg(all(feature = "web", target_arch = "wasm32"))]
{
return web_base_path();
}

read_env_config!("DIOXUS_ASSET_ROOT")
}

/// Get the path where the application is served from in the browser.
#[cfg(feature = "web")]
pub fn web_base_path() -> Option<String> {
// In debug mode, we get the base path from the meta element which can be hot reloaded and changed without recompiling
#[cfg(debug_assertions)]
{
thread_local! {
static BASE_PATH: std::cell::OnceCell<Option<String>> = const { std::cell::OnceCell::new() };
}
BASE_PATH.with(|f| f.get_or_init(|| get_meta_contents(ASSET_ROOT_ENV)).clone())
}

// In release mode, we get the base path from the environment variable
#[cfg(not(debug_assertions))]
{
option_env!("DIOXUS_ASSET_ROOT").map(ToString::to_string)
}
}

pub fn format_base_path_meta_element(base_path: &str) -> String {
format!(r#"<meta name="{ASSET_ROOT_ENV}" content="{base_path}">"#,)
}

pub fn out_dir() -> Option<PathBuf> {
Expand Down
10 changes: 10 additions & 0 deletions packages/cli/src/build/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{assets::AssetManifest, TraceSrc};
use crate::{link::LinkAction, BuildArgs};
use crate::{AppBundle, Platform};
use anyhow::Context;
use dioxus_cli_config::{APP_TITLE_ENV, ASSET_ROOT_ENV};
use serde::Deserialize;
use std::{
path::{Path, PathBuf},
Expand Down Expand Up @@ -540,6 +541,15 @@ impl BuildRequest {
// env_vars.push(("PATH", extended_path));
};

// If this is a release build, bake the base path and title
// into the binary with env vars
if self.build.release {
if let Some(base_path) = &self.krate.config.web.app.base_path {
env_vars.push((ASSET_ROOT_ENV, base_path.clone()));
}
env_vars.push((APP_TITLE_ENV, self.krate.config.web.app.title.clone()));
}

Ok(env_vars)
}

Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/build/web.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use dioxus_cli_config::format_base_path_meta_element;

use crate::error::Result;
use crate::BuildRequest;
use std::fmt::Write;
Expand Down Expand Up @@ -66,6 +68,13 @@ impl BuildRequest {
)?;
}

// Add the base path to the head if this is a debug build
if self.is_dev_build() {
if let Some(base_path) = &self.krate.config.web.app.base_path {
head_resources.push_str(&format_base_path_meta_element(base_path));
}
}

if !style_list.is_empty() {
self.send_resource_deprecation_warning(style_list, ResourceType::Style);
}
Expand Down
12 changes: 9 additions & 3 deletions packages/cli/src/config/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,15 @@ pub(crate) struct WebAppConfig {
impl WebAppConfig {
/// Get the normalized base path for the application with `/` trimmed from both ends. If the base path is not set, this will return `.`.
pub(crate) fn base_path(&self) -> &str {
match &self.base_path {
Some(path) => path.trim_matches('/'),
None => ".",
let trimmed_path = self
.base_path
.as_deref()
.unwrap_or_default()
.trim_matches('/');
if trimmed_path.is_empty() {
"."
} else {
trimmed_path
}
}
}
Expand Down
14 changes: 11 additions & 3 deletions packages/cli/src/serve/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ impl AppHandle {
// Set the env vars that the clients will expect
// These need to be stable within a release version (ie 0.6.0)
let mut envs = vec![
("DIOXUS_CLI_ENABLED", "true".to_string()),
(dioxus_cli_config::CLI_ENABLED_ENV, "true".to_string()),
(
dioxus_cli_config::APP_TITLE_ENV,
self.app.build.krate.config.web.app.title.clone(),
),
("RUST_BACKTRACE", "1".to_string()),
(
dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV,
Expand All @@ -80,6 +84,10 @@ impl AppHandle {
("CARGO_MANIFEST_DIR", "".to_string()),
];

if let Some(base_path) = &self.app.build.krate.config.web.app.base_path {
envs.push((dioxus_cli_config::ASSET_ROOT_ENV, base_path.clone()));
}

if let Some(addr) = fullstack_address {
envs.push((dioxus_cli_config::SERVER_IP_ENV, addr.ip().to_string()));
envs.push((dioxus_cli_config::SERVER_PORT_ENV, addr.port().to_string()));
Expand Down Expand Up @@ -109,7 +117,7 @@ impl AppHandle {
Platform::Web => {
// Only the first build we open the web app, after that the user knows it's running
if open_browser {
self.open_web(envs, devserver_ip);
self.open_web(devserver_ip);
}

None
Expand Down Expand Up @@ -212,7 +220,7 @@ impl AppHandle {
/// Open the web app by opening the browser to the given address.
/// Check if we need to use https or not, and if so, add the protocol.
/// Go to the basepath if that's set too.
fn open_web(&self, _envs: Vec<(&str, String)>, address: SocketAddr) {
fn open_web(&self, address: SocketAddr) {
let base_path = self.app.build.krate.config.web.app.base_path.clone();
let https = self
.app
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/serve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
}

ServeUpdate::OpenApp => {
runner.open_existing(&devserver).await;
if let Err(err) = runner.open_existing(&devserver).await {
tracing::error!("Failed to open app: {err}")
}
}

ServeUpdate::Redraw => {
Expand Down
13 changes: 8 additions & 5 deletions packages/cli/src/serve/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,15 @@ impl AppRunner {
}

/// Open an existing app bundle, if it exists
pub(crate) async fn open_existing(&self, devserver: &WebServer) {
if let Some(address) = devserver.server_address() {
let url = format!("http://{address}");
tracing::debug!("opening url: {url}");
_ = open::that(url);
pub(crate) async fn open_existing(&mut self, devserver: &WebServer) -> Result<()> {
if let Some((_, app)) = self
.running
.iter_mut()
.find(|(platform, _)| **platform != Platform::Server)
{
app.open(devserver.devserver_address(), None, true).await?;
}
Ok(())
}

pub(crate) fn attempt_hot_reload(
Expand Down
3 changes: 2 additions & 1 deletion packages/dioxus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dioxus-ssr = { workspace = true, optional = true }
manganis = { workspace = true, features = ["dioxus"], optional = true }

serde = { version = "1.0.136", optional = true }
dioxus-cli-config = { workspace = true, optional = true }

[target.'cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")))'.dependencies]
dioxus-devtools = { workspace = true, optional = true }
Expand All @@ -53,7 +54,7 @@ router = ["dep:dioxus-router"]
fullstack = ["dep:dioxus-fullstack", "dioxus-config-macro/fullstack", "dep:serde"]
desktop = ["dep:dioxus-desktop", "dioxus-fullstack?/desktop", "dioxus-config-macro/desktop"]
mobile = ["dep:dioxus-mobile", "dioxus-fullstack?/mobile", "dioxus-config-macro/mobile"]
web = ["dep:dioxus-web", "dioxus-fullstack?/web", "dioxus-config-macro/web"]
web = ["dep:dioxus-web", "dioxus-fullstack?/web", "dioxus-config-macro/web", "dioxus-cli-config", "dioxus-cli-config/web"]
ssr = ["dep:dioxus-ssr", "dioxus-config-macro/ssr"]
liveview = ["dep:dioxus-liveview", "dioxus-config-macro/liveview"]
server = ["dioxus-fullstack?/axum", "dioxus-fullstack?/server", "ssr", "dioxus-liveview?/axum"]
Expand Down
13 changes: 13 additions & 0 deletions packages/dioxus/src/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ fn web_launch(
.unwrap_or_default()
.hydrate(true);

// If there is a base path set, call server functions from that base path
if let Some(base_path) = dioxus_cli_config::web_base_path() {
let base_path = base_path.trim_matches('/');
crate::prelude::server_fn::client::set_server_url(
format!(
"{}/{}",
crate::prelude::server_fn::client::get_server_url(),
base_path
)
.leak(),
);
}

let factory = move || {
let mut vdom = dioxus_core::VirtualDom::new(root);
for context in contexts {
Expand Down
17 changes: 14 additions & 3 deletions packages/fullstack/src/render.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! A shared pool of renderers for efficient server side rendering.
use crate::document::ServerDocument;
use crate::streaming::{Mount, StreamingRenderer};
use dioxus_cli_config::base_path;
use dioxus_interpreter_js::INITIALIZE_STREAMING_JS;
use dioxus_isrg::{CachedRender, RenderFreshness};
use dioxus_lib::document::Document;
Expand Down Expand Up @@ -169,9 +170,19 @@ impl SsrRendererPool {
let mut virtual_dom = virtual_dom_factory();
let document = std::rc::Rc::new(crate::document::server::ServerDocument::default());
virtual_dom.provide_root_context(document.clone());
virtual_dom.provide_root_context(Rc::new(
dioxus_history::MemoryHistory::with_initial_path(&route),
) as Rc<dyn dioxus_history::History>);
// If there is a base path, trim the base path from the route and add the base path formatting to the
// history provider
let history;
if let Some(base_path) = base_path() {
let base_path = base_path.trim_matches('/');
let base_path = format!("/{base_path}");
let route = route.strip_prefix(&base_path).unwrap_or(&route);
history =
dioxus_history::MemoryHistory::with_initial_path(route).with_prefix(base_path);
} else {
history = dioxus_history::MemoryHistory::with_initial_path(&route);
}
virtual_dom.provide_root_context(Rc::new(history) as Rc<dyn dioxus_history::History>);
virtual_dom.provide_root_context(document.clone() as std::rc::Rc<dyn Document>);

// poll the future, which may call server_context()
Expand Down
40 changes: 38 additions & 2 deletions packages/fullstack/src/server/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

use std::any::Any;

use axum::{
body::Body,
extract::{Request, State},
response::IntoResponse,
};
use dioxus_cli_config::base_path;
use dioxus_lib::prelude::*;

use crate::server::{render_handler, RenderHandleState, SSRState};

/// Launch a fullstack app with the given root component, contexts, and config.
#[allow(unused)]
pub fn launch(
Expand Down Expand Up @@ -60,9 +68,37 @@ pub fn launch(
}
}

#[allow(unused_mut)]
let mut router =
let mut base_path = base_path();
let config = platform_config.as_ref().ok().cloned();
let dioxus_router =
axum::Router::new().serve_dioxus_application(TryIntoResult(platform_config), root);
let mut router;
match base_path.as_deref() {
Some(base_path) => {
let base_path = base_path.trim_matches('/');
// If there is a base path, nest the router under it and serve the root route manually
// Nesting a route in axum only serves /base_path or /base_path/ not both
router = axum::Router::new().nest(&format!("/{base_path}/"), dioxus_router);
async fn root_render_handler(
state: State<RenderHandleState>,
mut request: Request<Body>,
) -> impl IntoResponse {
// The root of the base path always looks like the root from dioxus fullstack
*request.uri_mut() = "/".parse().unwrap();
render_handler(state, request).await
}
if let Some(cfg) = config {
let ssr_state = SSRState::new(&cfg);
router = router.route(
&format!("/{base_path}"),
axum::routing::method_routing::get(root_render_handler).with_state(
RenderHandleState::new(cfg, root).with_ssr_state(ssr_state),
),
)
}
}
None => router = dioxus_router,
}

let router = router.into_make_service();
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
Expand Down
Loading
Loading