From 3fb1f739d13967da9ee8db9f17a91fea321c9df2 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Tue, 17 Sep 2024 17:18:23 -0700 Subject: [PATCH] Simplify cli-config, hotreload -> devtools (drop to 0 deps, fast compile times) (#2975) * simplify cli-config crate * clean up configs * add devtools crate, update cargo imports * fix serve addr, fix websocket proxy issue * add comment about opt profiles * rename hot-reload to devtools --- .vscode/settings.json | 7 +- Cargo.lock | 273 ++----- Cargo.toml | 19 +- example-projects/ecommerce-site/Cargo.toml | 12 +- example-projects/ecommerce-site/src/api.rs | 4 - packages/cli-config/Cargo.toml | 32 +- packages/cli-config/README.md | 10 +- packages/cli-config/build.rs | 3 - packages/cli-config/src/bundle.rs | 260 ------- packages/cli-config/src/config.rs | 372 ---------- packages/cli-config/src/lib.rs | 146 ++-- packages/cli-config/src/serve.rs | 98 --- packages/cli/Cargo.toml | 9 +- packages/cli/src/builder/cargo.rs | 6 +- packages/cli/src/builder/mod.rs | 49 +- packages/cli/src/builder/web.rs | 4 +- packages/cli/src/cli/build.rs | 2 +- packages/cli/src/cli/serve.rs | 2 +- packages/cli/src/config.rs | 668 ++++++++++++++++++ packages/cli/src/dioxus_crate.rs | 33 +- packages/cli/src/main.rs | 1 + packages/cli/src/serve/mod.rs | 17 +- packages/cli/src/serve/output.rs | 4 +- packages/cli/src/serve/output/render.rs | 2 +- packages/cli/src/serve/proxy.rs | 51 +- packages/cli/src/serve/server.rs | 5 +- packages/cli/src/serve/watcher.rs | 2 +- packages/desktop/Cargo.toml | 11 +- packages/desktop/src/app.rs | 43 +- packages/desktop/src/config.rs | 14 +- packages/desktop/src/ipc.rs | 4 +- packages/desktop/src/launch.rs | 7 +- packages/desktop/src/protocol.rs | 8 +- packages/desktop/src/webview.rs | 2 +- packages/devtools-types/Cargo.toml | 8 + .../{hot-reload => devtools-types}/src/lib.rs | 28 - packages/devtools/Cargo.toml | 31 + packages/devtools/src/lib.rs | 59 ++ packages/dioxus/Cargo.toml | 6 +- packages/dioxus/src/lib.rs | 6 +- packages/fullstack/Cargo.toml | 11 +- packages/fullstack/src/launch.rs | 9 +- packages/hot-reload/Cargo.toml | 52 -- packages/hot-reload/README.md | 171 ----- packages/hot-reload/src/client.rs | 25 - packages/hot-reload/src/ws_receiver.rs | 73 -- packages/liveview/Cargo.toml | 8 +- packages/liveview/src/config.rs | 14 +- packages/liveview/src/pool.rs | 27 +- packages/router/Cargo.toml | 2 +- packages/router/src/history/web.rs | 15 +- packages/rsx/.vscode/settings.json | 4 - packages/static-generation/Cargo.toml | 6 +- packages/static-generation/src/launch.rs | 13 +- packages/static-generation/src/ssg.rs | 5 +- packages/web/Cargo.toml | 6 +- .../web/src/{hot_reload.rs => devtools.rs} | 2 +- packages/web/src/lib.rs | 24 +- 58 files changed, 1131 insertions(+), 1654 deletions(-) delete mode 100644 packages/cli-config/build.rs delete mode 100644 packages/cli-config/src/bundle.rs delete mode 100644 packages/cli-config/src/config.rs delete mode 100644 packages/cli-config/src/serve.rs create mode 100644 packages/cli/src/config.rs create mode 100644 packages/devtools-types/Cargo.toml rename packages/{hot-reload => devtools-types}/src/lib.rs (67%) create mode 100644 packages/devtools/Cargo.toml create mode 100644 packages/devtools/src/lib.rs delete mode 100644 packages/hot-reload/Cargo.toml delete mode 100644 packages/hot-reload/README.md delete mode 100644 packages/hot-reload/src/client.rs delete mode 100644 packages/hot-reload/src/ws_receiver.rs rename packages/web/src/{hot_reload.rs => devtools.rs} (99%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 319a96943..a1b7ed88b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,10 +4,11 @@ "editor.formatOnSave": false }, // "rust-analyzer.check.workspace": true, - "rust-analyzer.check.workspace": false, - "rust-analyzer.check.features": "all", + // "rust-analyzer.check.workspace": false, + // "rust-analyzer.check.features": "all", "rust-analyzer.cargo.features": "all", - "rust-analyzer.check.allTargets": true, + "rust-analyzer.check.features": "all", + // "rust-analyzer.check.allTargets": true, // we don't want the formatter to kick in while we're working on dioxus itself "dioxus.formatOnSave": "disabled", } diff --git a/Cargo.lock b/Cargo.lock index 3d0129c07..ea3c8f33a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1225,42 +1225,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cached" -version = "0.44.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b195e4fbc4b6862bbd065b991a34750399c119797efff72492f28a5864de8700" -dependencies = [ - "async-trait", - "cached_proc_macro", - "cached_proc_macro_types", - "futures", - "hashbrown 0.13.2", - "instant", - "once_cell", - "thiserror", - "tokio", -] - -[[package]] -name = "cached_proc_macro" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b48814962d2fd604c50d2b9433c2a41a0ab567779ee2c02f7fba6eca1221f082" -dependencies = [ - "cached_proc_macro_types", - "darling 0.14.4", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cached_proc_macro_types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" - [[package]] name = "cairo-rs" version = "0.18.5" @@ -2247,38 +2211,14 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.10", - "darling_macro 0.20.10", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -2295,24 +2235,13 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core 0.14.4", - "quote", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.10", + "darling_core", "quote", "syn 2.0.77", ] @@ -2437,9 +2366,9 @@ dependencies = [ "dioxus-core", "dioxus-core-macro", "dioxus-desktop", + "dioxus-devtools", "dioxus-fullstack", "dioxus-hooks", - "dioxus-hot-reload", "dioxus-html", "dioxus-liveview", "dioxus-mobile", @@ -2511,7 +2440,7 @@ dependencies = [ "dioxus-cli-config", "dioxus-core", "dioxus-core-types", - "dioxus-hot-reload", + "dioxus-devtools", "dioxus-html", "dioxus-rsx", "dioxus-rsx-hotreload", @@ -2540,7 +2469,7 @@ dependencies = [ "ratatui", "rayon", "regex", - "reqwest 0.12.7", + "reqwest", "rustls 0.23.13", "serde", "serde_json", @@ -2548,6 +2477,7 @@ dependencies = [ "syn 2.0.77", "tar", "tauri-bundler", + "tauri-utils", "tempfile", "thiserror", "tokio", @@ -2568,19 +2498,6 @@ dependencies = [ [[package]] name = "dioxus-cli-config" version = "0.6.0-alpha.2" -dependencies = [ - "built", - "cargo_toml", - "clap", - "dirs", - "once_cell", - "serde", - "serde_json", - "tauri-bundler", - "tauri-utils", - "toml 0.8.19", - "tracing", -] [[package]] name = "dioxus-config-macro" @@ -2605,7 +2522,7 @@ dependencies = [ "manganis", "pretty_assertions", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest", "rustc-hash 1.1.0", "rustversion", "serde", @@ -2649,8 +2566,8 @@ dependencies = [ "dioxus", "dioxus-cli-config", "dioxus-core", + "dioxus-devtools", "dioxus-hooks", - "dioxus-hot-reload", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", @@ -2667,7 +2584,7 @@ dependencies = [ "objc", "objc_id", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest", "rfd", "rustc-hash 1.1.0", "separator", @@ -2685,6 +2602,29 @@ dependencies = [ "wry", ] +[[package]] +name = "dioxus-devtools" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "dioxus-devtools-types", + "dioxus-signals", + "serde", + "serde_json", + "tokio", + "tracing", + "tungstenite 0.23.0", + "warnings", +] + +[[package]] +name = "dioxus-devtools-types" +version = "0.6.0-alpha.2" +dependencies = [ + "dioxus-core", + "serde", +] + [[package]] name = "dioxus-examples" version = "0.6.0-alpha.2" @@ -2700,7 +2640,7 @@ dependencies = [ "http-range", "manganis", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest", "separator", "serde", "serde_json", @@ -2730,11 +2670,10 @@ dependencies = [ "base64 0.22.1", "bytes", "ciborium", - "clap", "dioxus", "dioxus-cli-config", "dioxus-desktop", - "dioxus-hot-reload", + "dioxus-devtools", "dioxus-interpreter-js", "dioxus-isrg", "dioxus-lib", @@ -2776,7 +2715,7 @@ dependencies = [ "futures-channel", "futures-util", "generational-box", - "reqwest 0.12.7", + "reqwest", "rustversion", "slab", "tokio", @@ -2785,31 +2724,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "dioxus-hot-reload" -version = "0.6.0-alpha.2" -dependencies = [ - "chrono", - "dioxus-cli-config", - "dioxus-core", - "dioxus-html", - "dioxus-rsx", - "dioxus-rsx-hotreload", - "dioxus-signals", - "execute", - "futures-util", - "ignore", - "notify", - "once_cell", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tokio-tungstenite 0.23.1", - "tracing", - "warnings", -] - [[package]] name = "dioxus-html" version = "0.6.0-alpha.2" @@ -2905,7 +2819,7 @@ dependencies = [ "dioxus", "dioxus-cli-config", "dioxus-core", - "dioxus-hot-reload", + "dioxus-devtools", "dioxus-html", "dioxus-interpreter-js", "futures-channel", @@ -3087,7 +3001,7 @@ dependencies = [ "once_cell", "parking_lot", "rand 0.8.5", - "reqwest 0.12.7", + "reqwest", "rustc-hash 1.1.0", "serde", "simple_logger", @@ -3116,8 +3030,8 @@ dependencies = [ "criterion", "dioxus", "dioxus-cli-config", + "dioxus-devtools", "dioxus-fullstack", - "dioxus-hot-reload", "dioxus-isrg", "dioxus-lib", "dioxus-router", @@ -3147,7 +3061,7 @@ dependencies = [ "dioxus", "dioxus-core", "dioxus-core-types", - "dioxus-hot-reload", + "dioxus-devtools", "dioxus-html", "dioxus-interpreter-js", "dioxus-signals", @@ -3301,10 +3215,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" name = "ecommerce-site" version = "0.1.1" dependencies = [ - "cached", "chrono", "dioxus", - "reqwest 0.11.27", + "reqwest", "serde", ] @@ -3374,7 +3287,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling 0.20.10", + "darling", "proc-macro2", "quote", "syn 2.0.77", @@ -3787,7 +3700,7 @@ version = "0.1.0" dependencies = [ "chrono", "dioxus", - "reqwest 0.12.7", + "reqwest", "serde", "tracing", "tracing-subscriber", @@ -3799,7 +3712,7 @@ name = "fullstack-hello-world-example" version = "0.1.0" dependencies = [ "dioxus", - "reqwest 0.12.7", + "reqwest", "serde", "simple_logger", "tracing", @@ -5242,19 +5155,6 @@ dependencies = [ "tokio-io-timeout", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.30", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -6236,7 +6136,7 @@ dependencies = [ "railwind", "ravif", "rayon", - "reqwest 0.12.7", + "reqwest", "rustc-hash 1.1.0", "serde", "serde_json", @@ -8245,46 +8145,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration 0.5.1", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg 0.50.0", -] - [[package]] name = "reqwest" version = "0.12.7" @@ -8303,7 +8163,7 @@ dependencies = [ "http-body-util", "hyper 1.4.1", "hyper-rustls", - "hyper-tls 0.6.0", + "hyper-tls", "hyper-util", "ipnet", "js-sys", @@ -8322,7 +8182,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper 1.0.1", - "system-configuration 0.6.1", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -8941,7 +8801,7 @@ version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ - "darling 0.20.10", + "darling", "proc-macro2", "quote", "syn 2.0.77", @@ -8965,7 +8825,7 @@ dependencies = [ "inventory", "js-sys", "once_cell", - "reqwest 0.12.7", + "reqwest", "send_wrapper", "serde", "serde_json", @@ -11214,17 +11074,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys 0.5.0", -] - [[package]] name = "system-configuration" version = "0.6.1" @@ -11233,17 +11082,7 @@ checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.6.0", "core-foundation 0.9.4", - "system-configuration-sys 0.6.0", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", + "system-configuration-sys", ] [[package]] @@ -11378,7 +11217,7 @@ dependencies = [ "uuid", "walkdir", "windows-sys 0.48.0", - "winreg 0.51.0", + "winreg", "zip", ] @@ -13247,16 +13086,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.51.0" diff --git a/Cargo.toml b/Cargo.toml index e29c35507..b408c9d09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,8 @@ members = [ "packages/rsx-rosetta", "packages/generational-box", "packages/signals", - "packages/hot-reload", + "packages/devtools", + "packages/devtools-types", "packages/fullstack", "packages/server-macro", "packages/static-generation", @@ -91,9 +92,10 @@ dioxus-rsx = { path = "packages/rsx", version = "0.6.0-alpha.0" } dioxus-rsx-hotreload = { path = "packages/rsx-hotreload", version = "0.6.0-alpha.0" } dioxus-rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.6.0-alpha.0" } dioxus-signals = { path = "packages/signals", version = "0.6.0-alpha.0" } -dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.0", default-features = false} +dioxus-cli-config = { path = "packages/cli-config", version = "0.6.0-alpha.0" } generational-box = { path = "packages/generational-box", version = "0.6.0-alpha.0" } -dioxus-hot-reload = { path = "packages/hot-reload", version = "0.6.0-alpha.0" } +dioxus-devtools = { path = "packages/devtools", version = "0.6.0-alpha.0" } +dioxus-devtools-types = { path = "packages/devtools-types", version = "0.6.0-alpha.0" } dioxus-fullstack = { path = "packages/fullstack", version = "0.6.0-alpha.1" } dioxus-static-site-generation = { path = "packages/static-generation", version = "0.6.0-alpha.0" } dioxus_server_macro = { path = "packages/server-macro", version = "0.6.0-alpha.0", default-features = false } @@ -171,6 +173,7 @@ web-sys = { version = "0.3.56", default-features = false } dirs = "5.0.1" cargo-config2 = "0.1.26" criterion = { version = "0.5" } +walrus = "*" # desktop wry = { version = "0.43.0", default-features = false } @@ -190,13 +193,9 @@ objc_id = "0.1.1" [profile.dev.package.dioxus-core-macro] opt-level = 3 -# Enable a small amount of optimization in debug mode -[profile.cli-dev] -inherits = "dev" -opt-level = 1 - -# Enable high optimizations for dependencies (incl. Bevy), but not for our code: -[profile.cli-dev.package."*"] +# wasm bindgen is slooooooow, but it's because we're actually processing the wasm +# so, lets just bump up walrus to make it faster, no need for any special profiles +[profile.dev.package.walrus] opt-level = 3 # Disable debug assertions to check the released path of core and other packages, but build without optimizations to keep build times quick diff --git a/example-projects/ecommerce-site/Cargo.toml b/example-projects/ecommerce-site/Cargo.toml index 97a541c42..45f63a8d0 100644 --- a/example-projects/ecommerce-site/Cargo.toml +++ b/example-projects/ecommerce-site/Cargo.toml @@ -6,11 +6,15 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cached = "0.44.0" dioxus = { workspace = true, features = ["fullstack", "router"] } -reqwest = { version = "0.11.16", features = ["json"] } -serde = "1.0.160" -chrono = { version = "0.4.24", features = ["serde"] } +reqwest = { workspace = true, features = ["json"] } +serde = { workspace = true } + +[target.'cfg(target_family = "wasm")'.dependencies] +chrono = { workspace = true, features = ["serde", "wasmbind"] } + +[target.'cfg(not(target_family = "wasm"))'.dependencies] +chrono = { workspace = true, features = ["serde"] } [features] web = ["dioxus/web"] diff --git a/example-projects/ecommerce-site/src/api.rs b/example-projects/ecommerce-site/src/api.rs index 4646d87a6..30d506e3f 100644 --- a/example-projects/ecommerce-site/src/api.rs +++ b/example-projects/ecommerce-site/src/api.rs @@ -55,7 +55,6 @@ impl Display for Sort { } // Cache up to 100 requests, invalidating them after 60 seconds -#[cached::proc_macro::cached(size = 100, time = 60, result = true)] pub(crate) async fn fetch_user_carts(user_id: usize) -> Result, reqwest::Error> { reqwest::get(format!( "https://fakestoreapi.com/carts/user/{user_id}?startdate=2019-12-10&enddate=2023-01-01" @@ -66,7 +65,6 @@ pub(crate) async fn fetch_user_carts(user_id: usize) -> Result, reqwes } // Cache up to 100 requests, invalidating them after 60 seconds -#[cached::proc_macro::cached(size = 100, time = 60, result = true)] pub(crate) async fn fetch_user(user_id: usize) -> dioxus::Result { Ok( reqwest::get(format!("https://fakestoreapi.com/users/{user_id}")) @@ -77,7 +75,6 @@ pub(crate) async fn fetch_user(user_id: usize) -> dioxus::Result { } // Cache up to 100 requests, invalidating them after 60 seconds -#[cached::proc_macro::cached(size = 100, time = 60, result = true)] pub(crate) async fn fetch_product(product_id: usize) -> dioxus::Result { Ok( reqwest::get(format!("https://fakestoreapi.com/products/{product_id}")) @@ -88,7 +85,6 @@ pub(crate) async fn fetch_product(product_id: usize) -> dioxus::Result } // Cache up to 100 requests, invalidating them after 60 seconds -#[cached::proc_macro::cached(size = 100, time = 60, result = true)] pub(crate) async fn fetch_products(count: usize, sort: Sort) -> dioxus::Result> { Ok(reqwest::get(format!( "https://fakestoreapi.com/products/?sort={sort}&limit={count}" diff --git a/packages/cli-config/Cargo.toml b/packages/cli-config/Cargo.toml index d22b31525..d5852aab7 100644 --- a/packages/cli-config/Cargo.toml +++ b/packages/cli-config/Cargo.toml @@ -1,36 +1,6 @@ [package] name = "dioxus-cli-config" -version = { workspace = true } -authors = ["Jonathan Kelley"] edition = "2021" -description = "Configuration for the Dioxus CLI" -repository = "https://github.com/DioxusLabs/dioxus/" -license = "MIT OR Apache-2.0" -keywords = ["react", "gui", "cli", "dioxus", "wasm"] +version.workspace = true [dependencies] -clap = { version = "4.2", features = ["derive"], optional = true } -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" -toml = { workspace = true, optional = true } -cargo_toml = { workspace = true, optional = true } -once_cell = "1.18.0" -tracing = { workspace = true } - -# bundling -tauri-bundler = { workspace = true, optional = true } -tauri-utils = { workspace = true, optional = true } - -dirs = { workspace = true, optional = true } - -[build-dependencies] -built = { version = "=0.7.4", features = ["git2"] } - -[features] -default = ["read-config"] -cli = ["dep:tauri-bundler", "dep:tauri-utils", "read-from-args", "dep:toml", "dep:cargo_toml", "dep:dirs"] -read-config = [] -read-from-args = ["dep:clap"] - -[package.metadata.docs.rs] -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/cli-config/README.md b/packages/cli-config/README.md index 8cad990e3..e544837bd 100644 --- a/packages/cli-config/README.md +++ b/packages/cli-config/README.md @@ -1,5 +1,7 @@ -
-

📦✨ Dioxus CLI Configuration

-
+# dioxus-cli-config -The **dioxus-cli-config** contains the configuration for the **dioxus-cli**. +A crate that provides key/value names and types for configuring Dioxus applications at runtime. + +This crate exists for us to very cleanly define the exact fields we want to pass down to Dioxus applications at runtime but without exposing the entire config object. + +This leads to faster compile times, smaller binaries, and a clearer distinction between the config and the application. diff --git a/packages/cli-config/build.rs b/packages/cli-config/build.rs deleted file mode 100644 index d8f91cb91..000000000 --- a/packages/cli-config/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - built::write_built_file().expect("Failed to acquire build-time information"); -} diff --git a/packages/cli-config/src/bundle.rs b/packages/cli-config/src/bundle.rs deleted file mode 100644 index 07c4fec21..000000000 --- a/packages/cli-config/src/bundle.rs +++ /dev/null @@ -1,260 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct BundleConfig { - pub identifier: Option, - pub publisher: Option, - pub icon: Option>, - pub resources: Option>, - pub copyright: Option, - pub category: Option, - pub short_description: Option, - pub long_description: Option, - pub external_bin: Option>, - pub deb: Option, - pub macos: Option, - pub windows: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::BundleSettings { - fn from(val: BundleConfig) -> Self { - tauri_bundler::BundleSettings { - identifier: val.identifier, - publisher: val.publisher, - icon: val.icon, - resources: val.resources, - copyright: val.copyright, - category: val.category.and_then(|c| c.parse().ok()), - short_description: val.short_description, - long_description: val.long_description, - external_bin: val.external_bin, - deb: val.deb.map(Into::into).unwrap_or_default(), - macos: val.macos.map(Into::into).unwrap_or_default(), - windows: val.windows.map(Into::into).unwrap_or_default(), - ..Default::default() - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct DebianSettings { - pub depends: Option>, - pub files: HashMap, - pub nsis: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::DebianSettings { - fn from(val: DebianSettings) -> Self { - tauri_bundler::DebianSettings { - depends: val.depends, - files: val.files, - desktop_template: None, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WixSettings { - pub language: Vec<(String, Option)>, - pub template: Option, - pub fragment_paths: Vec, - pub component_group_refs: Vec, - pub component_refs: Vec, - pub feature_group_refs: Vec, - pub feature_refs: Vec, - pub merge_refs: Vec, - pub skip_webview_install: bool, - pub license: Option, - pub enable_elevated_update_task: bool, - pub banner_path: Option, - pub dialog_image_path: Option, - pub fips_compliant: bool, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::WixSettings { - fn from(val: WixSettings) -> Self { - tauri_bundler::WixSettings { - language: tauri_bundler::bundle::WixLanguage({ - let mut languages: Vec<_> = val - .language - .iter() - .map(|l| { - ( - l.0.clone(), - tauri_bundler::bundle::WixLanguageConfig { - locale_path: l.1.clone(), - }, - ) - }) - .collect(); - if languages.is_empty() { - languages.push(("en-US".into(), Default::default())); - } - languages - }), - template: val.template, - fragment_paths: val.fragment_paths, - component_group_refs: val.component_group_refs, - component_refs: val.component_refs, - feature_group_refs: val.feature_group_refs, - feature_refs: val.feature_refs, - merge_refs: val.merge_refs, - skip_webview_install: val.skip_webview_install, - license: val.license, - enable_elevated_update_task: val.enable_elevated_update_task, - banner_path: val.banner_path, - dialog_image_path: val.dialog_image_path, - fips_compliant: val.fips_compliant, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct MacOsSettings { - pub frameworks: Option>, - pub minimum_system_version: Option, - pub license: Option, - pub exception_domain: Option, - pub signing_identity: Option, - pub provider_short_name: Option, - pub entitlements: Option, - pub info_plist_path: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::MacOsSettings { - fn from(val: MacOsSettings) -> Self { - tauri_bundler::MacOsSettings { - frameworks: val.frameworks, - minimum_system_version: val.minimum_system_version, - license: val.license, - exception_domain: val.exception_domain, - signing_identity: val.signing_identity, - provider_short_name: val.provider_short_name, - entitlements: val.entitlements, - info_plist_path: val.info_plist_path, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WindowsSettings { - pub digest_algorithm: Option, - pub certificate_thumbprint: Option, - pub timestamp_url: Option, - pub tsp: bool, - pub wix: Option, - pub icon_path: Option, - pub webview_install_mode: WebviewInstallMode, - pub webview_fixed_runtime_path: Option, - pub allow_downgrades: bool, - pub nsis: Option, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::WindowsSettings { - fn from(val: WindowsSettings) -> Self { - tauri_bundler::WindowsSettings { - digest_algorithm: val.digest_algorithm, - certificate_thumbprint: val.certificate_thumbprint, - timestamp_url: val.timestamp_url, - tsp: val.tsp, - wix: val.wix.map(Into::into), - icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()), - webview_install_mode: val.webview_install_mode.into(), - webview_fixed_runtime_path: val.webview_fixed_runtime_path, - allow_downgrades: val.allow_downgrades, - nsis: val.nsis.map(Into::into), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct NsisSettings { - pub template: Option, - pub license: Option, - pub header_image: Option, - pub sidebar_image: Option, - pub installer_icon: Option, - pub install_mode: NSISInstallerMode, - pub languages: Option>, - pub custom_language_files: Option>, - pub display_language_selector: bool, -} - -#[cfg(feature = "cli")] -impl From for tauri_bundler::NsisSettings { - fn from(val: NsisSettings) -> Self { - tauri_bundler::NsisSettings { - license: val.license, - header_image: val.header_image, - sidebar_image: val.sidebar_image, - installer_icon: val.installer_icon, - install_mode: val.install_mode.into(), - languages: val.languages, - display_language_selector: val.display_language_selector, - custom_language_files: None, - template: None, - compression: None, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum NSISInstallerMode { - CurrentUser, - PerMachine, - Both, -} - -#[cfg(feature = "cli")] -impl From for tauri_utils::config::NSISInstallerMode { - fn from(val: NSISInstallerMode) -> Self { - match val { - NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser, - NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine, - NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum WebviewInstallMode { - Skip, - DownloadBootstrapper { silent: bool }, - EmbedBootstrapper { silent: bool }, - OfflineInstaller { silent: bool }, - FixedRuntime { path: PathBuf }, -} - -#[cfg(feature = "cli")] -impl WebviewInstallMode { - fn into(self) -> tauri_utils::config::WebviewInstallMode { - match self { - Self::Skip => tauri_utils::config::WebviewInstallMode::Skip, - Self::DownloadBootstrapper { silent } => { - tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent } - } - Self::EmbedBootstrapper { silent } => { - tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent } - } - Self::OfflineInstaller { silent } => { - tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent } - } - Self::FixedRuntime { path } => { - tauri_utils::config::WebviewInstallMode::FixedRuntime { path } - } - } - } -} - -impl Default for WebviewInstallMode { - fn default() -> Self { - Self::OfflineInstaller { silent: false } - } -} diff --git a/packages/cli-config/src/config.rs b/packages/cli-config/src/config.rs deleted file mode 100644 index 5ef83a630..000000000 --- a/packages/cli-config/src/config.rs +++ /dev/null @@ -1,372 +0,0 @@ -use crate::BundleConfig; -use serde::{Deserialize, Serialize}; -use std::fmt::Display; -use std::path::PathBuf; -use std::str::FromStr; - -#[derive( - Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, -)] -#[cfg_attr(feature = "cli", derive(clap::ValueEnum))] -#[non_exhaustive] -pub enum Platform { - /// Targeting the web platform using WASM - #[cfg_attr(feature = "cli", clap(name = "web"))] - #[serde(rename = "web")] - #[default] - Web, - - /// Targeting the desktop platform using Tao/Wry-based webview - #[cfg_attr(feature = "cli", clap(name = "desktop"))] - #[serde(rename = "desktop")] - Desktop, - - /// Targeting the server platform using Axum and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "fullstack"))] - #[serde(rename = "fullstack")] - Fullstack, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "static-generation"))] - #[serde(rename = "static-generation")] - StaticGeneration, - - /// Targeting the static generation platform using SSR and Dioxus-Fullstack - #[cfg_attr(feature = "cli", clap(name = "liveview"))] - #[serde(rename = "liveview")] - Liveview, -} - -/// An error that occurs when a platform is not recognized -pub struct UnknownPlatformError; - -impl std::fmt::Display for UnknownPlatformError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Unknown platform") - } -} - -impl FromStr for Platform { - type Err = UnknownPlatformError; - - fn from_str(s: &str) -> Result { - match s { - "web" => Ok(Self::Web), - "desktop" => Ok(Self::Desktop), - "fullstack" => Ok(Self::Fullstack), - "static-generation" => Ok(Self::StaticGeneration), - "liveview" => Ok(Self::Liveview), - _ => Err(UnknownPlatformError), - } - } -} - -impl Display for Platform { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let feature = self.feature_name(); - f.write_str(feature) - } -} - -impl Platform { - /// All platforms the dioxus CLI supports - pub const ALL: &'static [Self] = &[ - Platform::Web, - Platform::Desktop, - Platform::Fullstack, - Platform::StaticGeneration, - ]; - - /// Get the feature name for the platform in the dioxus crate - pub fn feature_name(&self) -> &str { - match self { - Platform::Web => "web", - Platform::Desktop => "desktop", - Platform::Fullstack => "fullstack", - Platform::StaticGeneration => "static-generation", - Platform::Liveview => "liveview", - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DioxusConfig { - pub application: ApplicationConfig, - - #[serde(default)] - pub web: WebConfig, - - #[serde(default)] - pub desktop: DesktopConfig, - - #[serde(default)] - pub bundle: BundleConfig, -} - -impl Default for DioxusConfig { - fn default() -> Self { - let name = default_name(); - Self { - application: ApplicationConfig { - name: name.clone(), - default_platform: default_platform(), - out_dir: out_dir_default(), - asset_dir: asset_dir_default(), - - sub_package: None, - }, - web: WebConfig { - app: WebAppConfig { - title: default_title(), - base_path: None, - }, - proxy: vec![], - watcher: Default::default(), - resource: WebResourceConfig { - dev: WebDevResourceConfig { - style: vec![], - script: vec![], - }, - style: Some(vec![]), - script: Some(vec![]), - }, - https: WebHttpsConfig { - enabled: None, - mkcert: None, - key_path: None, - cert_path: None, - }, - pre_compress: true, - wasm_opt: Default::default(), - }, - desktop: DesktopConfig::default(), - bundle: BundleConfig { - identifier: Some(format!("io.github.{name}")), - publisher: Some(name), - ..Default::default() - }, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ApplicationConfig { - #[serde(default = "default_name")] - pub name: String, - - #[serde(default = "default_platform")] - pub default_platform: Platform, - - #[serde(default = "out_dir_default")] - pub out_dir: PathBuf, - - #[serde(default = "asset_dir_default")] - pub asset_dir: PathBuf, - - #[serde(default)] - pub sub_package: Option, -} - -fn default_name() -> String { - "my-cool-project".into() -} - -fn default_platform() -> Platform { - Platform::Web -} - -fn asset_dir_default() -> PathBuf { - PathBuf::from("public") -} - -fn out_dir_default() -> PathBuf { - PathBuf::from("dist") -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebConfig { - #[serde(default)] - pub app: WebAppConfig, - #[serde(default)] - pub proxy: Vec, - #[serde(default)] - pub watcher: WebWatcherConfig, - #[serde(default)] - pub resource: WebResourceConfig, - #[serde(default)] - pub https: WebHttpsConfig, - /// Whether to enable pre-compression of assets and wasm during a web build in release mode - #[serde(default = "true_bool")] - pub pre_compress: bool, - /// The wasm-opt configuration - #[serde(default)] - pub wasm_opt: WasmOptConfig, -} - -impl Default for WebConfig { - fn default() -> Self { - Self { - pre_compress: true_bool(), - app: Default::default(), - https: Default::default(), - wasm_opt: Default::default(), - proxy: Default::default(), - watcher: Default::default(), - resource: Default::default(), - } - } -} - -/// Represents configuration items for the desktop platform. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct DesktopConfig { - /// Describes whether a debug-mode desktop app should be always-on-top. - #[serde(default)] - pub always_on_top: bool, -} - -impl Default for DesktopConfig { - fn default() -> Self { - Self { - always_on_top: true, - } - } -} - -/// The wasm-opt configuration -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct WasmOptConfig { - /// The wasm-opt level to use for release builds [default: s] - /// Options: - /// - z: optimize aggressively for size - /// - s: optimize for size - /// - 1: optimize for speed - /// - 2: optimize for more for speed - /// - 3: optimize for even more for speed - /// - 4: optimize aggressively for speed - #[serde(default)] - pub level: WasmOptLevel, - - /// Keep debug symbols in the wasm file - #[serde(default = "false_bool")] - pub debug: bool, -} - -/// The wasm-opt level to use for release web builds [default: 4] -#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] -pub enum WasmOptLevel { - /// Optimize aggressively for size - #[serde(rename = "z")] - Z, - /// Optimize for size - #[serde(rename = "s")] - S, - /// Don't optimize - #[serde(rename = "0")] - Zero, - /// Optimize for speed - #[serde(rename = "1")] - One, - /// Optimize for more for speed - #[serde(rename = "2")] - Two, - /// Optimize for even more for speed - #[serde(rename = "3")] - Three, - /// Optimize aggressively for speed - #[serde(rename = "4")] - #[default] - Four, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebAppConfig { - #[serde(default = "default_title")] - pub title: String, - pub base_path: Option, -} - -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 fn base_path(&self) -> &str { - match &self.base_path { - Some(path) => path.trim_matches('/'), - None => ".", - } - } -} - -impl Default for WebAppConfig { - fn default() -> Self { - Self { - title: default_title(), - base_path: None, - } - } -} - -fn default_title() -> String { - "dioxus | ⛺".into() -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebProxyConfig { - pub backend: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct WebWatcherConfig { - #[serde(default = "watch_path_default")] - pub watch_path: Vec, - - #[serde(default)] - pub reload_html: bool, - - #[serde(default = "true_bool")] - pub index_on_404: bool, -} - -impl Default for WebWatcherConfig { - fn default() -> Self { - Self { - watch_path: watch_path_default(), - reload_html: false, - index_on_404: true, - } - } -} - -fn watch_path_default() -> Vec { - vec![PathBuf::from("src"), PathBuf::from("examples")] -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebResourceConfig { - pub dev: WebDevResourceConfig, - pub style: Option>, - pub script: Option>, -} - -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct WebDevResourceConfig { - #[serde(default)] - pub style: Vec, - #[serde(default)] - pub script: Vec, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct WebHttpsConfig { - pub enabled: Option, - pub mkcert: Option, - pub key_path: Option, - pub cert_path: Option, -} - -fn true_bool() -> bool { - true -} - -fn false_bool() -> bool { - false -} diff --git a/packages/cli-config/src/lib.rs b/packages/cli-config/src/lib.rs index 91c3f646a..70ee0711d 100644 --- a/packages/cli-config/src/lib.rs +++ b/packages/cli-config/src/lib.rs @@ -1,99 +1,73 @@ -#![doc = include_str!("../README.md")] -#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] -#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + path::PathBuf, +}; -mod config; -pub use config::*; +pub const CLI_ENABLED_ENV: &str = "DIOXUS_CLI_ENABLED"; +pub const SERVER_IP_ENV: &str = "IP"; +pub const SERVER_PORT_ENV: &str = "PORT"; +pub const DEVSERVER_RAW_ADDR_ENV: &str = "DIOXUS_DEVSERVER_ADDR"; +pub const ALWAYS_ON_TOP_ENV: &str = "DIOXUS_ALWAYS_ON_TOP"; +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"; -mod bundle; -pub use bundle::*; +/// todo: this is not implemented but we're going to reserve this +/// +/// technically this is only passed on "launch" so if you close the app, this will be lost +pub const IOS_DEVSERVER_ADDR_ENV: &str = "SIMCTL_CHILD_DIOXUS_DEVSERVER_ADDR"; -mod serve; -pub use serve::*; - -mod build_info; - -#[doc(hidden)] -pub mod __private { - use crate::DioxusConfig; - - pub(crate) const DIOXUS_CLI_VERSION: &str = "DIOXUS_CLI_VERSION"; - pub(crate) const CONFIG_ENV: &str = "DIOXUS_CONFIG"; - pub(crate) const CONFIG_BASE_PATH_ENV: &str = "DIOXUS_CONFIG_BASE_PATH"; - - pub fn save_config(config: &DioxusConfig, cli_version: &str) -> CrateConfigDropGuard { - std::env::set_var(CONFIG_ENV, serde_json::to_string(config).unwrap()); - std::env::set_var( - CONFIG_BASE_PATH_ENV, - config.web.app.base_path.clone().unwrap_or_default(), - ); - std::env::set_var(DIOXUS_CLI_VERSION, cli_version); - CrateConfigDropGuard - } - - /// A guard that removes the config from the environment when dropped. - pub struct CrateConfigDropGuard; - - impl Drop for CrateConfigDropGuard { - fn drop(&mut self) { - std::env::remove_var(CONFIG_ENV); - std::env::remove_var(CONFIG_BASE_PATH_ENV); - std::env::remove_var(DIOXUS_CLI_VERSION); - } - } - - #[cfg(feature = "read-config")] - /// The environment variable that stores the CLIs serve configuration. - /// We use this to communicate between the CLI and the server for fullstack applications. - pub const SERVE_ENV: &str = "DIOXUS_SERVE_CONFIG"; +/// Get the address of the devserver for use over a raw socket +/// +/// This is not a websocket! There's no protocol! +pub fn devserver_raw_addr() -> Option { + std::env::var(DEVSERVER_RAW_ADDR_ENV) + .map(|s| s.parse().ok()) + .ok() + .flatten() } -/// An error that occurs when the dioxus CLI was not used to build the application. -#[derive(Debug)] -pub struct DioxusCLINotUsed; - -impl std::fmt::Display for DioxusCLINotUsed { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("dioxus CLI was not used to build the application") - } +pub fn devserver_ws_endpoint() -> Option { + let addr = devserver_raw_addr()?; + Some(format!("ws://{addr}/_dioxus")) } -impl std::error::Error for DioxusCLINotUsed {} +pub fn server_ip() -> Option { + std::env::var(SERVER_IP_ENV) + .ok() + .and_then(|s| s.parse().ok()) +} -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub static CURRENT_CONFIG: once_cell::sync::Lazy< - Result, -> = once_cell::sync::Lazy::new(|| { - CURRENT_CONFIG_JSON - .ok_or_else(|| { - tracing::warn!("A library is trying to access the crate's configuration, but the dioxus CLI was not used to build the application."); - DioxusCLINotUsed -}).and_then( - |config| - match serde_json::from_str(config) { - Ok(config) => Ok(config), - Err(err) => { - let mut cli_version = crate::build_info::PKG_VERSION.to_string(); +pub fn server_port() -> Option { + std::env::var(SERVER_PORT_ENV) + .ok() + .and_then(|s| s.parse().ok()) +} - if let Some(hash) = crate::build_info::GIT_COMMIT_HASH_SHORT { - let hash = &hash.trim_start_matches('g')[..4]; - cli_version.push_str(&format!("-{hash}")); - } +pub fn fullstack_address_or_localhost() -> SocketAddr { + let ip = server_ip().unwrap_or_else(|| IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))); + let port = server_port().unwrap_or(8080); + SocketAddr::new(ip, port) +} - let dioxus_version = std::option_env!("DIOXUS_CLI_VERSION").unwrap_or("unknown"); +pub fn app_title() -> Option { + std::env::var(APP_TITLE_ENV).ok() +} - tracing::warn!("Failed to parse the CLI config file. This is likely caused by a mismatch between the version of the CLI and the dioxus version.\nCLI version: {cli_version}\nDioxus version: {dioxus_version}\nSerialization error: {err}"); - Err(DioxusCLINotUsed) - } - } -) -}); +pub fn always_on_top() -> Option { + std::env::var(ALWAYS_ON_TOP_ENV) + .ok() + .and_then(|s| s.parse().ok()) +} -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub const CURRENT_CONFIG_JSON: Option<&str> = std::option_env!("DIOXUS_CONFIG"); +pub fn is_cli_enabled() -> bool { + std::env::var(CLI_ENABLED_ENV).is_ok() +} -#[cfg(feature = "read-config")] -/// The current crate's configuration. -pub const BASE_PATH: Option<&str> = std::option_env!("DIOXUS_CONFIG_BASE_PATH"); +pub fn base_path() -> Option { + std::env::var("DIOXUS_ASSET_ROOT").ok().map(PathBuf::from) +} + +pub fn out_dir() -> Option { + std::env::var(OUT_DIR).ok().map(PathBuf::from) +} diff --git a/packages/cli-config/src/serve.rs b/packages/cli-config/src/serve.rs deleted file mode 100644 index a8894c72a..000000000 --- a/packages/cli-config/src/serve.rs +++ /dev/null @@ -1,98 +0,0 @@ -#![allow(unused)] // lots of configs... - -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; - -#[cfg(feature = "read-from-args")] -use clap::Parser; - -/// The arguments for the address the server will run on - -#[cfg(feature = "read-from-args")] -#[derive(Clone, Debug, Parser)] -pub struct AddressArguments { - /// The port the server will run on - #[clap(long)] - #[clap(default_value_t = default_port())] - pub port: u16, - - /// The address the server will run on - #[clap(long, default_value_t = default_address())] - pub addr: std::net::IpAddr, -} - -#[cfg(feature = "read-from-args")] -impl Default for AddressArguments { - fn default() -> Self { - Self { - port: default_port(), - addr: default_address(), - } - } -} - -#[cfg(feature = "read-from-args")] -impl AddressArguments { - /// Get the address the server should run on - pub fn address(&self) -> SocketAddr { - SocketAddr::new(self.addr, self.port) - } -} - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct RuntimeCLIArguments { - /// The address hot reloading is running on - cli_address: SocketAddr, - - /// The address the server should run on - server_socket: Option, -} - -impl RuntimeCLIArguments { - /// Create a new RuntimeCLIArguments - pub fn new(cli_address: SocketAddr, server_socket: Option) -> Self { - Self { - cli_address, - server_socket, - } - } - - /// Attempt to read the current serve settings from the CLI. This will only be set for the fullstack platform on recent versions of the CLI. - pub fn from_cli() -> Option { - std::env::var(crate::__private::SERVE_ENV) - .ok() - .and_then(|json| serde_json::from_str(&json).ok()) - } - - /// Get the address the server should run on - pub fn server_socket(&self) -> Option { - self.server_socket - } - - /// Get the address the CLI is running on - pub fn cli_address(&self) -> SocketAddr { - self.cli_address - } - - /// Get the address the proxied fullstack server should run on - #[cfg(feature = "read-from-args")] - pub fn fullstack_address(&self) -> AddressArguments { - let socket = self.server_socket.unwrap_or_else(|| { - SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, default_port())) - }); - - AddressArguments { - port: socket.port(), - addr: socket.ip(), - } - } -} - -#[cfg(feature = "read-from-args")] -fn default_port() -> u16 { - 8080 -} - -#[cfg(feature = "read-from-args")] -fn default_address() -> IpAddr { - IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) -} diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 57c172d42..0530c7ba3 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -16,7 +16,7 @@ thiserror = { workspace = true } wasm-bindgen-cli-support = "0.2" wasm-bindgen-shared = "0.2" colored = "2.0.0" -dioxus-cli-config = { workspace = true, features = ["cli"], default-features = false } +dioxus-cli-config = { workspace = true } # features log = "0.4.14" @@ -78,6 +78,7 @@ toml_edit = "0.22.20" # bundling tauri-bundler = { workspace = true } +tauri-utils = { workspace = true } # formatting # syn = { workspace = true } @@ -95,7 +96,7 @@ dioxus-rsx-hotreload = { workspace = true } dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-core-types = { workspace = true } -dioxus-hot-reload = { workspace = true, features = ["serve"] } +dioxus-devtools = { workspace = true } ignore = "0.4.22" env_logger = "0.11.3" @@ -119,9 +120,7 @@ built = { version = "=0.7.4", features = ["git2"] } default = [] plugin = [] tokio-console = ["dep:console-subscriber"] - -# when releasing dioxus, we want to enable wasm-opt -wasm-opt = ["dep:wasm-opt"] +# optimizations = ["dep:wasm-opt"] [[bin]] path = "src/main.rs" diff --git a/packages/cli/src/builder/cargo.rs b/packages/cli/src/builder/cargo.rs index 804391818..c8471994e 100644 --- a/packages/cli/src/builder/cargo.rs +++ b/packages/cli/src/builder/cargo.rs @@ -10,11 +10,11 @@ use crate::builder::progress::CargoBuildResult; use crate::builder::progress::Stage; use crate::builder::progress::UpdateBuildProgress; use crate::builder::progress::UpdateStage; +use crate::config::Platform; use crate::link::LinkCommand; use crate::Result; use crate::TraceSrc; use anyhow::Context; -use dioxus_cli_config::Platform; use futures_channel::mpsc::UnboundedSender; use manganis_cli_support::AssetManifest; use manganis_cli_support::ManganisSupportGuard; @@ -115,10 +115,6 @@ impl BuildRequest { let hash = &hash.trim_start_matches('g')[..4]; dioxus_version.push_str(&format!("-{hash}")); } - let _guard = dioxus_cli_config::__private::save_config( - &self.dioxus_crate.dioxus_config, - &dioxus_version, - ); let _manganis_support = ManganisSupportGuard::default(); let _asset_guard = AssetConfigDropGuard::new(self.dioxus_crate.dioxus_config.web.app.base_path.as_deref()); diff --git a/packages/cli/src/builder/mod.rs b/packages/cli/src/builder/mod.rs index 82e702a40..d245f05a8 100644 --- a/packages/cli/src/builder/mod.rs +++ b/packages/cli/src/builder/mod.rs @@ -1,8 +1,8 @@ use crate::cli::serve::ServeArguments; +use crate::config::Platform; use crate::dioxus_crate::DioxusCrate; use crate::Result; use crate::{build::Build, TraceSrc}; -use dioxus_cli_config::{Platform, RuntimeCLIArguments}; use futures_util::stream::select_all; use futures_util::StreamExt; use std::net::SocketAddr; @@ -96,7 +96,6 @@ impl BuildRequest { Platform::StaticGeneration | Platform::Fullstack => { Self::new_fullstack(dioxus_crate.clone(), build_arguments, serve)? } - _ => unimplemented!("Unknown platform: {platform:?}"), }) } @@ -159,11 +158,16 @@ pub(crate) struct BuildResult { impl BuildResult { /// Open the executable if this is a native build + #[allow(clippy::too_many_arguments)] pub fn open( &self, serve: &ServeArguments, fullstack_address: Option, workspace: &std::path::Path, + asset_root: &std::path::Path, + devserver_addr: SocketAddr, + app_title: String, + out_dir: PathBuf, ) -> std::io::Result> { match self.target_platform { TargetPlatform::Web => { @@ -174,13 +178,7 @@ impl BuildResult { tracing::info!(dx_src = ?TraceSrc::Dev, "Launching desktop app at {} 🎉", self.executable.display()); } TargetPlatform::Server => { - if let Some(fullstack_address) = fullstack_address { - tracing::info!( - dx_src = ?TraceSrc::Dev, - "Launching fullstack server on http://{:?} 🎉", - fullstack_address - ); - } + // shut this up for now - the web app will take priority in logging } TargetPlatform::Liveview => { if let Some(fullstack_address) = fullstack_address { @@ -195,19 +193,36 @@ impl BuildResult { tracing::info!(dx_src = ?TraceSrc::Dev, "Press [o] to open the app manually."); - let arguments = RuntimeCLIArguments::new(serve.address.address(), fullstack_address); let executable = self.executable.canonicalize()?; let mut cmd = Command::new(executable); - cmd - // When building the fullstack server, we need to forward the serve arguments (like port) to the fullstack server through env vars - .env( - dioxus_cli_config::__private::SERVE_ENV, - serde_json::to_string(&arguments).unwrap(), - ) - .stderr(Stdio::piped()) + + // Set the env vars that the clients will expect + // These need to be stable within a release version (ie 0.6.0) + cmd.env(dioxus_cli_config::CLI_ENABLED_ENV, "true"); + if let Some(addr) = fullstack_address { + cmd.env(dioxus_cli_config::SERVER_IP_ENV, addr.ip().to_string()); + cmd.env(dioxus_cli_config::SERVER_PORT_ENV, addr.port().to_string()); + } + cmd.env( + dioxus_cli_config::ALWAYS_ON_TOP_ENV, + serve.always_on_top.unwrap_or(true).to_string(), + ); + cmd.env( + dioxus_cli_config::ASSET_ROOT_ENV, + asset_root.display().to_string(), + ); + cmd.env( + dioxus_cli_config::DEVSERVER_RAW_ADDR_ENV, + devserver_addr.to_string(), + ); + cmd.env(dioxus_cli_config::APP_TITLE_ENV, app_title); + cmd.env(dioxus_cli_config::OUT_DIR, out_dir.display().to_string()); + + cmd.stderr(Stdio::piped()) .stdout(Stdio::piped()) .kill_on_drop(true) .current_dir(workspace); + Ok(Some(cmd.spawn()?)) } } diff --git a/packages/cli/src/builder/web.rs b/packages/cli/src/builder/web.rs index 0354d8637..b9f4ac7eb 100644 --- a/packages/cli/src/builder/web.rs +++ b/packages/cli/src/builder/web.rs @@ -132,11 +132,11 @@ impl BuildRequest { // Only run wasm-opt if the feature is enabled // Wasm-opt has an expensive build script that makes it annoying to keep enabled for iterative dev - #[cfg(feature = "wasm-opt")] + #[cfg(feature = "optimizations")] { // Run wasm-opt if this is a release build if self.build_arguments.release { - use dioxus_cli_config::WasmOptLevel; + use crate::config::WasmOptLevel; tracing::info!(dx_src = ?TraceSrc::Build, "Running optimization with wasm-opt..."); let mut options = match self.dioxus_crate.dioxus_config.web.wasm_opt.level { diff --git a/packages/cli/src/cli/build.rs b/packages/cli/src/cli/build.rs index f8e96dc71..8e7f6099c 100644 --- a/packages/cli/src/cli/build.rs +++ b/packages/cli/src/cli/build.rs @@ -1,7 +1,7 @@ use std::str::FromStr; +use crate::config::Platform; use anyhow::Context; -use dioxus_cli_config::Platform; use crate::{ builder::{BuildRequest, TargetPlatform}, diff --git a/packages/cli/src/cli/serve.rs b/packages/cli/src/cli/serve.rs index a55e19414..eac315648 100644 --- a/packages/cli/src/cli/serve.rs +++ b/packages/cli/src/cli/serve.rs @@ -1,7 +1,7 @@ +use crate::config::AddressArguments; use crate::{settings, tracer::CLILogControl, DioxusCrate}; use anyhow::Context; use build::Build; -use dioxus_cli_config::AddressArguments; use std::ops::Deref; use super::*; diff --git a/packages/cli/src/config.rs b/packages/cli/src/config.rs new file mode 100644 index 000000000..657f8d1bf --- /dev/null +++ b/packages/cli/src/config.rs @@ -0,0 +1,668 @@ +use clap::Parser; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::fmt::Display; +use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; +use std::str::FromStr; + +#[derive( + Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug, Default, +)] +#[derive(clap::ValueEnum)] +#[non_exhaustive] +pub enum Platform { + /// Targeting the web platform using WASM + #[clap(name = "web")] + #[serde(rename = "web")] + #[default] + Web, + + /// Targeting the desktop platform using Tao/Wry-based webview + #[clap(name = "desktop")] + #[serde(rename = "desktop")] + Desktop, + + /// Targeting the server platform using Axum and Dioxus-Fullstack + #[clap(name = "fullstack")] + #[serde(rename = "fullstack")] + Fullstack, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[ clap(name = "static-generation")] + #[serde(rename = "static-generation")] + StaticGeneration, + + /// Targeting the static generation platform using SSR and Dioxus-Fullstack + #[clap(name = "liveview")] + #[serde(rename = "liveview")] + Liveview, +} + +/// An error that occurs when a platform is not recognized +pub struct UnknownPlatformError; + +impl std::error::Error for UnknownPlatformError {} + +impl std::fmt::Debug for UnknownPlatformError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown platform") + } +} +impl std::fmt::Display for UnknownPlatformError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Unknown platform") + } +} + +impl FromStr for Platform { + type Err = UnknownPlatformError; + + fn from_str(s: &str) -> Result { + match s { + "web" => Ok(Self::Web), + "desktop" => Ok(Self::Desktop), + "fullstack" => Ok(Self::Fullstack), + "static-generation" => Ok(Self::StaticGeneration), + "liveview" => Ok(Self::Liveview), + _ => Err(UnknownPlatformError), + } + } +} + +impl Display for Platform { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let feature = self.feature_name(); + f.write_str(feature) + } +} + +impl Platform { + /// All platforms the dioxus CLI supports + pub const ALL: &'static [Self] = &[ + Platform::Web, + Platform::Desktop, + Platform::Fullstack, + Platform::StaticGeneration, + ]; + + /// Get the feature name for the platform in the dioxus crate + pub fn feature_name(&self) -> &str { + match self { + Platform::Web => "web", + Platform::Desktop => "desktop", + Platform::Fullstack => "fullstack", + Platform::StaticGeneration => "static-generation", + Platform::Liveview => "liveview", + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DioxusConfig { + pub application: ApplicationConfig, + + #[serde(default)] + pub web: WebConfig, + + #[serde(default)] + pub desktop: DesktopConfig, + + #[serde(default)] + pub bundle: BundleConfig, +} + +impl Default for DioxusConfig { + fn default() -> Self { + let name = default_name(); + Self { + application: ApplicationConfig { + name: name.clone(), + default_platform: default_platform(), + out_dir: out_dir_default(), + asset_dir: asset_dir_default(), + + sub_package: None, + }, + web: WebConfig { + app: WebAppConfig { + title: default_title(), + base_path: None, + }, + proxy: vec![], + watcher: Default::default(), + resource: WebResourceConfig { + dev: WebDevResourceConfig { + style: vec![], + script: vec![], + }, + style: Some(vec![]), + script: Some(vec![]), + }, + https: WebHttpsConfig { + enabled: None, + mkcert: None, + key_path: None, + cert_path: None, + }, + pre_compress: true, + wasm_opt: Default::default(), + }, + desktop: DesktopConfig::default(), + bundle: BundleConfig { + identifier: Some(format!("io.github.{name}")), + publisher: Some(name), + ..Default::default() + }, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ApplicationConfig { + #[serde(default = "default_name")] + pub name: String, + + #[serde(default = "default_platform")] + pub default_platform: Platform, + + #[serde(default = "out_dir_default")] + pub out_dir: PathBuf, + + #[serde(default = "asset_dir_default")] + pub asset_dir: PathBuf, + + #[serde(default)] + pub sub_package: Option, +} + +fn default_name() -> String { + "my-cool-project".into() +} + +fn default_platform() -> Platform { + Platform::Web +} + +fn asset_dir_default() -> PathBuf { + PathBuf::from("public") +} + +fn out_dir_default() -> PathBuf { + PathBuf::from("dist") +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebConfig { + #[serde(default)] + pub app: WebAppConfig, + #[serde(default)] + pub proxy: Vec, + #[serde(default)] + pub watcher: WebWatcherConfig, + #[serde(default)] + pub resource: WebResourceConfig, + #[serde(default)] + pub https: WebHttpsConfig, + /// Whether to enable pre-compression of assets and wasm during a web build in release mode + #[serde(default = "true_bool")] + pub pre_compress: bool, + /// The wasm-opt configuration + #[serde(default)] + pub wasm_opt: WasmOptConfig, +} + +impl Default for WebConfig { + fn default() -> Self { + Self { + pre_compress: true_bool(), + app: Default::default(), + https: Default::default(), + wasm_opt: Default::default(), + proxy: Default::default(), + watcher: Default::default(), + resource: Default::default(), + } + } +} + +/// Represents configuration items for the desktop platform. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DesktopConfig { + /// Describes whether a debug-mode desktop app should be always-on-top. + #[serde(default)] + pub always_on_top: bool, +} + +impl Default for DesktopConfig { + fn default() -> Self { + Self { + always_on_top: true, + } + } +} + +/// The wasm-opt configuration +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct WasmOptConfig { + /// The wasm-opt level to use for release builds [default: s] + /// Options: + /// - z: optimize aggressively for size + /// - s: optimize for size + /// - 1: optimize for speed + /// - 2: optimize for more for speed + /// - 3: optimize for even more for speed + /// - 4: optimize aggressively for speed + #[serde(default)] + pub level: WasmOptLevel, + + /// Keep debug symbols in the wasm file + #[serde(default = "false_bool")] + pub debug: bool, +} + +/// The wasm-opt level to use for release web builds [default: 4] +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] +pub enum WasmOptLevel { + /// Optimize aggressively for size + #[serde(rename = "z")] + Z, + /// Optimize for size + #[serde(rename = "s")] + S, + /// Don't optimize + #[serde(rename = "0")] + Zero, + /// Optimize for speed + #[serde(rename = "1")] + One, + /// Optimize for more for speed + #[serde(rename = "2")] + Two, + /// Optimize for even more for speed + #[serde(rename = "3")] + Three, + /// Optimize aggressively for speed + #[serde(rename = "4")] + #[default] + Four, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebAppConfig { + #[serde(default = "default_title")] + pub title: String, + pub base_path: Option, +} + +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 fn base_path(&self) -> &str { + match &self.base_path { + Some(path) => path.trim_matches('/'), + None => ".", + } + } +} + +impl Default for WebAppConfig { + fn default() -> Self { + Self { + title: default_title(), + base_path: None, + } + } +} + +fn default_title() -> String { + "dioxus | ⛺".into() +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebProxyConfig { + pub backend: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WebWatcherConfig { + #[serde(default = "watch_path_default")] + pub watch_path: Vec, + + #[serde(default)] + pub reload_html: bool, + + #[serde(default = "true_bool")] + pub index_on_404: bool, +} + +impl Default for WebWatcherConfig { + fn default() -> Self { + Self { + watch_path: watch_path_default(), + reload_html: false, + index_on_404: true, + } + } +} + +fn watch_path_default() -> Vec { + vec![PathBuf::from("src"), PathBuf::from("examples")] +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct WebResourceConfig { + pub dev: WebDevResourceConfig, + pub style: Option>, + pub script: Option>, +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct WebDevResourceConfig { + #[serde(default)] + pub style: Vec, + #[serde(default)] + pub script: Vec, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct WebHttpsConfig { + pub enabled: Option, + pub mkcert: Option, + pub key_path: Option, + pub cert_path: Option, +} + +fn true_bool() -> bool { + true +} + +fn false_bool() -> bool { + false +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct BundleConfig { + pub identifier: Option, + pub publisher: Option, + pub icon: Option>, + pub resources: Option>, + pub copyright: Option, + pub category: Option, + pub short_description: Option, + pub long_description: Option, + pub external_bin: Option>, + pub deb: Option, + pub macos: Option, + pub windows: Option, +} + +impl From for tauri_bundler::BundleSettings { + fn from(val: BundleConfig) -> Self { + tauri_bundler::BundleSettings { + identifier: val.identifier, + publisher: val.publisher, + icon: val.icon, + resources: val.resources, + copyright: val.copyright, + category: val.category.and_then(|c| c.parse().ok()), + short_description: val.short_description, + long_description: val.long_description, + external_bin: val.external_bin, + deb: val.deb.map(Into::into).unwrap_or_default(), + macos: val.macos.map(Into::into).unwrap_or_default(), + windows: val.windows.map(Into::into).unwrap_or_default(), + ..Default::default() + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct DebianSettings { + pub depends: Option>, + pub files: HashMap, + pub nsis: Option, +} + +impl From for tauri_bundler::DebianSettings { + fn from(val: DebianSettings) -> Self { + tauri_bundler::DebianSettings { + depends: val.depends, + files: val.files, + desktop_template: None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct WixSettings { + pub language: Vec<(String, Option)>, + pub template: Option, + pub fragment_paths: Vec, + pub component_group_refs: Vec, + pub component_refs: Vec, + pub feature_group_refs: Vec, + pub feature_refs: Vec, + pub merge_refs: Vec, + pub skip_webview_install: bool, + pub license: Option, + pub enable_elevated_update_task: bool, + pub banner_path: Option, + pub dialog_image_path: Option, + pub fips_compliant: bool, +} + +impl From for tauri_bundler::WixSettings { + fn from(val: WixSettings) -> Self { + tauri_bundler::WixSettings { + language: tauri_bundler::bundle::WixLanguage({ + let mut languages: Vec<_> = val + .language + .iter() + .map(|l| { + ( + l.0.clone(), + tauri_bundler::bundle::WixLanguageConfig { + locale_path: l.1.clone(), + }, + ) + }) + .collect(); + if languages.is_empty() { + languages.push(("en-US".into(), Default::default())); + } + languages + }), + template: val.template, + fragment_paths: val.fragment_paths, + component_group_refs: val.component_group_refs, + component_refs: val.component_refs, + feature_group_refs: val.feature_group_refs, + feature_refs: val.feature_refs, + merge_refs: val.merge_refs, + skip_webview_install: val.skip_webview_install, + license: val.license, + enable_elevated_update_task: val.enable_elevated_update_task, + banner_path: val.banner_path, + dialog_image_path: val.dialog_image_path, + fips_compliant: val.fips_compliant, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MacOsSettings { + pub frameworks: Option>, + pub minimum_system_version: Option, + pub license: Option, + pub exception_domain: Option, + pub signing_identity: Option, + pub provider_short_name: Option, + pub entitlements: Option, + pub info_plist_path: Option, +} + +impl From for tauri_bundler::MacOsSettings { + fn from(val: MacOsSettings) -> Self { + tauri_bundler::MacOsSettings { + frameworks: val.frameworks, + minimum_system_version: val.minimum_system_version, + license: val.license, + exception_domain: val.exception_domain, + signing_identity: val.signing_identity, + provider_short_name: val.provider_short_name, + entitlements: val.entitlements, + info_plist_path: val.info_plist_path, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowsSettings { + pub digest_algorithm: Option, + pub certificate_thumbprint: Option, + pub timestamp_url: Option, + pub tsp: bool, + pub wix: Option, + pub icon_path: Option, + pub webview_install_mode: WebviewInstallMode, + pub webview_fixed_runtime_path: Option, + pub allow_downgrades: bool, + pub nsis: Option, +} + +impl From for tauri_bundler::WindowsSettings { + fn from(val: WindowsSettings) -> Self { + tauri_bundler::WindowsSettings { + digest_algorithm: val.digest_algorithm, + certificate_thumbprint: val.certificate_thumbprint, + timestamp_url: val.timestamp_url, + tsp: val.tsp, + wix: val.wix.map(Into::into), + icon_path: val.icon_path.unwrap_or("icons/icon.ico".into()), + webview_install_mode: val.webview_install_mode.into(), + webview_fixed_runtime_path: val.webview_fixed_runtime_path, + allow_downgrades: val.allow_downgrades, + nsis: val.nsis.map(Into::into), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct NsisSettings { + pub template: Option, + pub license: Option, + pub header_image: Option, + pub sidebar_image: Option, + pub installer_icon: Option, + pub install_mode: NSISInstallerMode, + pub languages: Option>, + pub custom_language_files: Option>, + pub display_language_selector: bool, +} + +impl From for tauri_bundler::NsisSettings { + fn from(val: NsisSettings) -> Self { + tauri_bundler::NsisSettings { + license: val.license, + header_image: val.header_image, + sidebar_image: val.sidebar_image, + installer_icon: val.installer_icon, + install_mode: val.install_mode.into(), + languages: val.languages, + display_language_selector: val.display_language_selector, + custom_language_files: None, + template: None, + compression: None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum NSISInstallerMode { + CurrentUser, + PerMachine, + Both, +} + +impl From for tauri_utils::config::NSISInstallerMode { + fn from(val: NSISInstallerMode) -> Self { + match val { + NSISInstallerMode::CurrentUser => tauri_utils::config::NSISInstallerMode::CurrentUser, + NSISInstallerMode::PerMachine => tauri_utils::config::NSISInstallerMode::PerMachine, + NSISInstallerMode::Both => tauri_utils::config::NSISInstallerMode::Both, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum WebviewInstallMode { + Skip, + DownloadBootstrapper { silent: bool }, + EmbedBootstrapper { silent: bool }, + OfflineInstaller { silent: bool }, + FixedRuntime { path: PathBuf }, +} + +impl WebviewInstallMode { + fn into(self) -> tauri_utils::config::WebviewInstallMode { + match self { + Self::Skip => tauri_utils::config::WebviewInstallMode::Skip, + Self::DownloadBootstrapper { silent } => { + tauri_utils::config::WebviewInstallMode::DownloadBootstrapper { silent } + } + Self::EmbedBootstrapper { silent } => { + tauri_utils::config::WebviewInstallMode::EmbedBootstrapper { silent } + } + Self::OfflineInstaller { silent } => { + tauri_utils::config::WebviewInstallMode::OfflineInstaller { silent } + } + Self::FixedRuntime { path } => { + tauri_utils::config::WebviewInstallMode::FixedRuntime { path } + } + } + } +} + +impl Default for WebviewInstallMode { + fn default() -> Self { + Self::OfflineInstaller { silent: false } + } +} + +/// The arguments for the address the server will run on + +#[derive(Clone, Debug, Parser)] +pub struct AddressArguments { + /// The port the server will run on + #[clap(long)] + #[clap(default_value_t = default_port())] + pub port: u16, + + /// The address the server will run on + #[clap(long, default_value_t = default_address())] + pub addr: std::net::IpAddr, +} + +impl Default for AddressArguments { + fn default() -> Self { + Self { + port: default_port(), + addr: default_address(), + } + } +} + +impl AddressArguments { + /// Get the address the server should run on + pub fn address(&self) -> SocketAddr { + SocketAddr::new(self.addr, self.port) + } +} + +fn default_port() -> u16 { + 8080 +} + +fn default_address() -> IpAddr { + IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)) +} diff --git a/packages/cli/src/dioxus_crate.rs b/packages/cli/src/dioxus_crate.rs index 3ecadd224..ad727f329 100644 --- a/packages/cli/src/dioxus_crate.rs +++ b/packages/cli/src/dioxus_crate.rs @@ -1,6 +1,6 @@ use crate::build::TargetArgs; -use dioxus_cli_config::{DioxusConfig, Platform}; -use krates::cm::Target; +use crate::config::{DioxusConfig, Platform}; +use krates::{cm::Target, KrateDetails}; use krates::{cm::TargetKind, Cmd, Krates, NodeId}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -84,16 +84,26 @@ fn find_main_package(package: Option, krates: &Krates) -> Result { let mut workspace_members = krates.workspace_members(); - workspace_members - .find_map(|node| { - if let krates::Node::Krate { id, krate, .. } = node { - if krate.name == package { - return Some(id); - } + let found = workspace_members.find_map(|node| { + if let krates::Node::Krate { id, krate, .. } = node { + if krate.name == package { + return Some(id); } - None - }) - .ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? + } + None + }); + + if found.is_none() { + eprintln!("Could not find package {package} in the workspace. Did you forget to add it to the workspace?"); + eprintln!("Packages in the workspace:"); + for package in krates.workspace_members() { + if let krates::Node::Krate { krate, .. } = package { + eprintln!("{}", krate.name()); + } + } + } + + found.ok_or_else(|| CrateConfigError::PackageNotFound(package.clone()))? } None => { // Otherwise find the package that is the closest parent of the current directory @@ -144,6 +154,7 @@ impl DioxusCrate { cmd.features(target.features.clone()); let builder = krates::Builder::new(); let krates = builder.build(cmd, |_| {})?; + let package = find_main_package(target.package.clone(), &krates)?; let dioxus_config = load_dioxus_config(&krates, package)?.unwrap_or_default(); diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index e2146a77d..5ea1332d3 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -5,6 +5,7 @@ pub mod assets; pub mod builder; pub mod cli; +pub mod config; pub mod dioxus_crate; pub mod dx_build_info; pub mod error; diff --git a/packages/cli/src/serve/mod.rs b/packages/cli/src/serve/mod.rs index 10e0868ae..de16c4c5a 100644 --- a/packages/cli/src/serve/mod.rs +++ b/packages/cli/src/serve/mod.rs @@ -155,9 +155,24 @@ pub async fn serve_all( builder.children.clear(); } + let asset_dir = dioxus_crate + .dioxus_config + .application + .asset_dir + .canonicalize() + .unwrap_or(std::path::PathBuf::from("./assets")); + // If we have a build result, open it for build_result in results.iter() { - let child = build_result.open(&serve.server_arguments, server.fullstack_address(), &dioxus_crate.workspace_dir()); + let child = build_result.open( + &serve.server_arguments, + server.fullstack_address(), + &dioxus_crate.workspace_dir(), + &asset_dir, + server.ip, + dioxus_crate.dioxus_config.application.name.clone(), + dioxus_crate.out_dir() + ); match child { Ok(Some(child_proc)) => builder.children.push((build_result.target_platform, child_proc)), Err(e) => { diff --git a/packages/cli/src/serve/output.rs b/packages/cli/src/serve/output.rs index 75d4a13c3..37deafa75 100644 --- a/packages/cli/src/serve/output.rs +++ b/packages/cli/src/serve/output.rs @@ -1,3 +1,4 @@ +use crate::config::{AddressArguments, Platform}; use crate::{ builder::{BuildResult, UpdateStage}, builder::{Stage, TargetPlatform, UpdateBuildProgress}, @@ -18,8 +19,7 @@ use crossterm::{ tty::IsTty, ExecutableCommand, }; -use dioxus_cli_config::{AddressArguments, Platform}; -use dioxus_hot_reload::ClientMsg; +use dioxus_devtools::ClientMsg; use futures_util::{future::select_all, Future, FutureExt, StreamExt}; use ratatui::{prelude::*, TerminalOptions, Viewport}; use std::{ diff --git a/packages/cli/src/serve/output/render.rs b/packages/cli/src/serve/output/render.rs index 02aa93fe2..6adfcae73 100644 --- a/packages/cli/src/serve/output/render.rs +++ b/packages/cli/src/serve/output/render.rs @@ -1,6 +1,6 @@ use super::{BuildProgress, TraceMsg, TraceSrc}; +use crate::config::Platform; use ansi_to_tui::IntoText as _; -use dioxus_cli_config::Platform; use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, style::{Color, Style, Stylize}, diff --git a/packages/cli/src/serve/proxy.rs b/packages/cli/src/serve/proxy.rs index bb3cb3e57..98194620a 100644 --- a/packages/cli/src/serve/proxy.rs +++ b/packages/cli/src/serve/proxy.rs @@ -1,10 +1,10 @@ +use crate::config::WebProxyConfig; use crate::TraceSrc; use crate::{Error, Result}; -use dioxus_cli_config::WebProxyConfig; use anyhow::{anyhow, Context}; -use axum::body::Body as MyBody; use axum::body::Body; +use axum::{body::Body as MyBody, response::IntoResponse}; use axum::{ http::StatusCode, routing::{any, MethodRouter}, @@ -97,7 +97,7 @@ pub(crate) fn proxy_to( nocache: bool, handle_error: fn(Error) -> Response, ) -> MethodRouter { - let client = ProxyClient::new(url); + let client = ProxyClient::new(url.clone()); any(move |mut req: Request| async move { // Prevent request loops @@ -115,11 +115,54 @@ pub(crate) fn proxy_to( "true".parse().expect("header value is valid"), ); + // We have to throw a redirect for ws connections since the upgrade handler will not be called + // Our _dioxus handler will override this in the default case + if req.uri().scheme().map(|f| f.as_str()) == Some("ws") + || req.uri().scheme().map(|f| f.as_str()) == Some("wss") + { + let new_host = url.host().unwrap_or("localhost"); + let proxied_uri = format!( + "{scheme}://{host}:{port}{path_and_query}", + scheme = req.uri().scheme_str().unwrap_or("ws"), + port = url.port().unwrap(), + host = new_host, + path_and_query = req + .uri() + .path_and_query() + .map(|f| f.to_string()) + .unwrap_or_default() + ); + tracing::info!(dx_src = ?TraceSrc::Dev, "Proxied websocket request {req:?} to {proxied_uri}"); + + return Ok(axum::response::Redirect::permanent(&proxied_uri).into_response()); + } + if nocache { crate::serve::insert_no_cache_headers(req.headers_mut()); } - client.send(req).await.map_err(handle_error) + let uri = req.uri().clone(); + let res = client.send(req).await.map_err(handle_error); + + match res { + Ok(res) => { + // log assets at a different log level + if uri.path().starts_with("/assets") + || uri.path().starts_with("/_dioxus") + || uri.path().starts_with("/public") + { + tracing::trace!(dx_src = ?TraceSrc::Dev, "[{}] {}", res.status().as_u16(), uri); + } else { + tracing::info!(dx_src = ?TraceSrc::Dev, "[{}] {}", res.status().as_u16(), uri); + } + + Ok(res.into_response()) + } + Err(err) => { + tracing::error!(dx_src = ?TraceSrc::Dev, "[{}] {}", err.status().as_u16(), uri); + Err(err) + } + } }) } diff --git a/packages/cli/src/serve/server.rs b/packages/cli/src/serve/server.rs index 26d98196e..b253db1b7 100644 --- a/packages/cli/src/serve/server.rs +++ b/packages/cli/src/serve/server.rs @@ -1,3 +1,4 @@ +use crate::config::{Platform, WebHttpsConfig}; use crate::serve::{next_or_pending, Serve}; use crate::{dioxus_crate::DioxusCrate, TraceSrc}; use crate::{Error, Result}; @@ -18,8 +19,7 @@ use axum::{ Extension, Router, }; use axum_server::tls_rustls::RustlsConfig; -use dioxus_cli_config::{Platform, WebHttpsConfig}; -use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; +use dioxus_devtools::{DevserverMsg, HotReloadMsg}; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::stream; use futures_util::{stream::FuturesUnordered, StreamExt}; @@ -419,6 +419,7 @@ fn setup_router( "/", get( |ws: WebSocketUpgrade, ext: Extension>| async move { + tracing::info!("Incoming hotreload websocket request: {ws:?}"); ws.on_upgrade(move |socket| async move { _ = ext.0.unbounded_send(socket) }) }, ), diff --git a/packages/cli/src/serve/watcher.rs b/packages/cli/src/serve/watcher.rs index 38581e909..0fb9ea4a6 100644 --- a/packages/cli/src/serve/watcher.rs +++ b/packages/cli/src/serve/watcher.rs @@ -5,7 +5,7 @@ use super::hot_reloading_file_map::HotreloadError; use crate::serve::hot_reloading_file_map::FileMap; use crate::TraceSrc; use crate::{cli::serve::Serve, dioxus_crate::DioxusCrate}; -use dioxus_hot_reload::HotReloadMsg; +use dioxus_devtools::HotReloadMsg; use dioxus_html::HtmlCtx; use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures_util::StreamExt; diff --git a/packages/desktop/Cargo.toml b/packages/desktop/Cargo.toml index 1b64bccfc..4bccbe18d 100644 --- a/packages/desktop/Cargo.toml +++ b/packages/desktop/Cargo.toml @@ -19,10 +19,10 @@ dioxus-html = { workspace = true, features = [ ] } dioxus-signals = { workspace = true, optional = true } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol", "serialize"] } -dioxus-cli-config = { workspace = true, features = ["read-config"] } +dioxus-cli-config = { workspace = true } generational-box = { workspace = true } # hotreload only works on desktop platforms.... mobile is still wip -dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"]} +dioxus-devtools = { workspace = true, optional = true } serde = "1.0.136" serde_json = "1.0.79" @@ -89,16 +89,15 @@ core-foundation = "0.9.3" objc = "0.2.7" [features] -default = ["tokio_runtime", "wry/objc-exception", "hot-reload", "devtools"] +default = ["tokio_runtime", "wry/objc-exception", "devtools"] tokio_runtime = ["dep:tokio"] fullscreen = ["wry/fullscreen"] transparent = ["wry/transparent"] -devtools = ["wry/devtools"] -hot-reload = ["dep:dioxus-hot-reload", "dioxus-signals"] +devtools = ["wry/devtools", "dep:dioxus-devtools", "dioxus-signals"] gnu = [] [package.metadata.docs.rs] -features = ["tokio_runtime", "hot-reload"] +features = ["tokio_runtime", "devtools"] cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] default-features = false targets = [ diff --git a/packages/desktop/src/app.rs b/packages/desktop/src/app.rs index 9b60f83ba..c35936cf9 100644 --- a/packages/desktop/src/app.rs +++ b/packages/desktop/src/app.rs @@ -86,12 +86,7 @@ impl App { app.set_menubar_receiver(); // Allow hotreloading to work - but only in debug mode - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "devtools", debug_assertions))] app.connect_hotreload(); #[cfg(debug_assertions)] @@ -140,25 +135,14 @@ impl App { } } - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "devtools", debug_assertions))] pub fn connect_hotreload(&self) { - let proxy = self.shared.proxy.clone(); - - tokio::task::spawn(async move { - let Some(Ok(mut receiver)) = dioxus_hot_reload::NativeReceiver::create_from_cli().await - else { - return; - }; - - while let Some(Ok(msg)) = receiver.next().await { + if let Some(endpoint) = dioxus_cli_config::devserver_ws_endpoint() { + let proxy = self.shared.proxy.clone(); + dioxus_devtools::connect(endpoint, move |msg| { _ = proxy.send_event(UserWindowEvent::HotReloadEvent(msg)); - } - }); + }) + } } pub fn handle_new_window(&mut self) { @@ -276,19 +260,14 @@ impl App { view.desktop_context.query.send(result); } - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] - pub fn handle_hot_reload_msg(&mut self, msg: dioxus_hot_reload::DevserverMsg) { - use dioxus_hot_reload::DevserverMsg; + #[cfg(all(feature = "devtools", debug_assertions))] + pub fn handle_hot_reload_msg(&mut self, msg: dioxus_devtools::DevserverMsg) { + use dioxus_devtools::DevserverMsg; match msg { DevserverMsg::HotReload(hr_msg) => { for webview in self.webviews.values_mut() { - dioxus_hot_reload::apply_changes(&mut webview.dom, &hr_msg); + dioxus_devtools::apply_changes(&webview.dom, &hr_msg); webview.poll_vdom(); } diff --git a/packages/desktop/src/config.rs b/packages/desktop/src/config.rs index ee201f924..1c2ffb234 100644 --- a/packages/desktop/src/config.rs +++ b/packages/desktop/src/config.rs @@ -65,18 +65,12 @@ impl Config { /// Initializes a new `WindowBuilder` with default values. #[inline] pub fn new() -> Self { - let dioxus_config = dioxus_cli_config::CURRENT_CONFIG.as_ref(); - - let mut window: WindowBuilder = WindowBuilder::new().with_title( - dioxus_config - .map(|c| c.application.name.clone()) - .unwrap_or("Dioxus App".to_string()), - ); + let mut window: WindowBuilder = WindowBuilder::new() + .with_title(dioxus_cli_config::app_title().unwrap_or_else(|| "Dioxus App".to_string())); // During development we want the window to be on top so we can see it while we work - let always_on_top = dioxus_config - .map(|c| c.desktop.always_on_top) - .unwrap_or(true); + let always_on_top = dioxus_cli_config::always_on_top().unwrap_or(true); + if cfg!(debug_assertions) { window = window.with_always_on_top(always_on_top); } diff --git a/packages/desktop/src/ipc.rs b/packages/desktop/src/ipc.rs index dcf7792a1..fcf5a0e4e 100644 --- a/packages/desktop/src/ipc.rs +++ b/packages/desktop/src/ipc.rs @@ -19,12 +19,12 @@ pub enum UserWindowEvent { /// Handle a hotreload event, basically telling us to update our templates #[cfg(all( - feature = "hot-reload", + feature = "devtools", debug_assertions, not(target_os = "android"), not(target_os = "ios") ))] - HotReloadEvent(dioxus_hot_reload::DevserverMsg), + HotReloadEvent(dioxus_devtools::DevserverMsg), /// Create a new window NewWindow, diff --git a/packages/desktop/src/launch.rs b/packages/desktop/src/launch.rs index 7d722e548..0bef0fc11 100644 --- a/packages/desktop/src/launch.rs +++ b/packages/desktop/src/launch.rs @@ -41,12 +41,7 @@ pub fn launch_virtual_dom_blocking(virtual_dom: VirtualDom, desktop_config: Conf #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] UserWindowEvent::MudaMenuEvent(evnt) => app.handle_menu_event(evnt), - #[cfg(all( - feature = "hot-reload", - debug_assertions, - not(target_os = "android"), - not(target_os = "ios") - ))] + #[cfg(all(feature = "devtools", debug_assertions))] UserWindowEvent::HotReloadEvent(msg) => app.handle_hot_reload_msg(msg), UserWindowEvent::Ipc { id, msg } => match msg.method() { diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index b2625084b..295a00e55 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -280,10 +280,10 @@ fn running_in_dev_mode() -> bool { #[allow(unreachable_code)] fn get_asset_root() -> Option { if running_in_dev_mode() { - return dioxus_cli_config::CURRENT_CONFIG - .as_ref() - .map(|c| c.application.out_dir.clone()) - .ok(); + // todo: we don't want to canonicalize assets like this, but it will take longer to migrate, so we'll do it later + // we should just be parsing paths the way they are instead of relative to the "asset" dir + // manganis will eventually just dump the raw path to us, but until then, we need to canonicalize here + return dioxus_cli_config::base_path(); } #[cfg(target_os = "macos")] diff --git a/packages/desktop/src/webview.rs b/packages/desktop/src/webview.rs index a680d28d5..6bc05c901 100644 --- a/packages/desktop/src/webview.rs +++ b/packages/desktop/src/webview.rs @@ -381,7 +381,7 @@ impl WebviewInstance { } } - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] pub fn kick_stylsheets(&self) { // run eval in the webview to kick the stylesheets by appending a query string // we should do something less clunky than this diff --git a/packages/devtools-types/Cargo.toml b/packages/devtools-types/Cargo.toml new file mode 100644 index 000000000..de88a9b9e --- /dev/null +++ b/packages/devtools-types/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "dioxus-devtools-types" +edition = "2021" +version.workspace = true + +[dependencies] +serde = { workspace = true, features = ["derive"] } +dioxus-core = { workspace = true } diff --git a/packages/hot-reload/src/lib.rs b/packages/devtools-types/src/lib.rs similarity index 67% rename from packages/hot-reload/src/lib.rs rename to packages/devtools-types/src/lib.rs index 1633d3a87..73989405d 100644 --- a/packages/hot-reload/src/lib.rs +++ b/packages/devtools-types/src/lib.rs @@ -2,18 +2,6 @@ use dioxus_core::internal::HotReloadTemplateWithLocation; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -#[cfg(feature = "client")] -mod client; - -#[cfg(feature = "client")] -pub use client::*; - -#[cfg(feature = "serve")] -mod ws_receiver; - -#[cfg(feature = "serve")] -pub use ws_receiver::*; - /// A message the hot reloading server sends to the client #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum DevserverMsg { @@ -49,21 +37,5 @@ pub enum ClientMsg { pub struct HotReloadMsg { pub templates: Vec, pub assets: Vec, - - /// A file changed that's not an asset or a rust file - best of luck! pub unknown_files: Vec, } - -#[test] -fn serialize_client_msg() { - let msg = ClientMsg::Log { - level: "info".to_string(), - messages: vec!["hello world".to_string()], - }; - - let json = serde_json::to_string(&msg).unwrap(); - assert_eq!( - json, - r#"{"Log":{"level":"info","messages":["hello world"]}}"# - ); -} diff --git a/packages/devtools/Cargo.toml b/packages/devtools/Cargo.toml new file mode 100644 index 000000000..4b6680dbe --- /dev/null +++ b/packages/devtools/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "dioxus-devtools" +authors = ["Jonathan Kelley", "Evan Almloff"] +version = { workspace = true } +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/DioxusLabs/dioxus/" +homepage = "https://dioxuslabs.com/learn/0.4/migration/hot_reload" +description = "Hot reloading utilities for Dioxus" +keywords = ["dom", "ui", "gui", "react", "hot-reloading"] + +[dependencies] +dioxus-signals = { workspace = true } +dioxus-core = { workspace = true, features = ["serialize"] } +dioxus-devtools-types = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } + +# hot reloading serve +tracing = { workspace = true } +warnings.workspace = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +tungstenite = { version = "0.23.0" } + +[dev-dependencies] +tokio = { workspace = true, features = ["full"] } +serde_json = "1.0.91" + +[package.metadata.docs.rs] +cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/devtools/src/lib.rs b/packages/devtools/src/lib.rs new file mode 100644 index 000000000..cdee491f6 --- /dev/null +++ b/packages/devtools/src/lib.rs @@ -0,0 +1,59 @@ +use dioxus_core::{ScopeId, VirtualDom}; +pub use dioxus_devtools_types::*; +use dioxus_signals::Writable; +use warnings::Warning; + +/// Applies template and literal changes to the VirtualDom +/// +/// Assets need to be handled by the renderer. +pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) { + dom.runtime().on_scope(ScopeId::ROOT, || { + let ctx = dioxus_signals::get_global_context(); + + for template in &msg.templates { + let id = &template.location; + let value = template.template.clone(); + if let Some(mut signal) = ctx.get_signal_with_key(id) { + dioxus_signals::warnings::signal_read_and_write_in_reactive_scope::allow(|| { + dioxus_signals::warnings::signal_write_in_component_body::allow(|| { + signal.set(Some(value)); + }); + }); + } + } + }); +} + +/// Connect to the devserver and handle its messages with a callback. +/// +/// This doesn't use any form of security or protocol, so it's not safe to expose to the internet. +#[cfg(not(target_arch = "wasm32"))] +pub fn connect(endpoint: String, mut callback: impl FnMut(DevserverMsg) + Send + 'static) { + std::thread::spawn(move || { + let (mut websocket, _req) = match tungstenite::connect(endpoint.clone()) { + Ok((websocket, req)) => (websocket, req), + Err(err) => { + eprintln!( + "Failed to connect to devserver at {} because {}", + endpoint, err + ); + return; + } + }; + + while let Ok(msg) = websocket.read() { + match msg { + tungstenite::Message::Text(text) => { + if let Ok(msg) = serde_json::from_str(&text) { + callback(msg); + } else { + eprintln!("Failed to parse message from devserver: {:?}", text); + } + } + msg => { + println!("Received a non-text message: {:?}", msg); + } + } + } + }); +} diff --git a/packages/dioxus/Cargo.toml b/packages/dioxus/Cargo.toml index 6e54668fd..5db3dda5e 100644 --- a/packages/dioxus/Cargo.toml +++ b/packages/dioxus/Cargo.toml @@ -31,16 +31,16 @@ serde = { version = "1.0.136", optional = true } axum = { workspace = true, optional = true } [target.'cfg(not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")))'.dependencies] -dioxus-hot-reload = { workspace = true, optional = true } +dioxus-devtools = { workspace = true, optional = true } [features] -default = ["macro", "html", "hot-reload", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] +default = ["macro", "html", "devtools", "signals", "hooks", "launch", "mounted", "file_engine", "document", "asset"] minimal = ["macro", "html", "signals", "hooks", "launch"] signals = ["dep:dioxus-signals"] macro = ["dep:dioxus-core-macro"] html = ["dep:dioxus-html"] hooks = ["dep:dioxus-hooks"] -hot-reload = ["dep:dioxus-hot-reload", "dioxus-web?/hot_reload", "dioxus-fullstack?/hot-reload"] +devtools = ["dep:dioxus-devtools", "dioxus-web?/devtools", "dioxus-fullstack?/devtools"] mounted = ["dioxus-web?/mounted", "dioxus-html?/mounted"] file_engine = ["dioxus-web?/file_engine"] asset = ["dep:manganis", "dioxus-core/manganis"] diff --git a/packages/dioxus/src/lib.rs b/packages/dioxus/src/lib.rs index f1231745d..24f231aec 100644 --- a/packages/dioxus/src/lib.rs +++ b/packages/dioxus/src/lib.rs @@ -93,10 +93,10 @@ pub mod prelude { #[cfg(all( not(any(target_arch = "wasm32", target_os = "ios", target_os = "android")), - feature = "hot-reload" + feature = "devtools" ))] - #[cfg_attr(docsrs, doc(cfg(feature = "hot-reload")))] - pub use dioxus_hot_reload; + #[cfg_attr(docsrs, doc(cfg(feature = "devtools")))] + pub use dioxus_devtools; pub use dioxus_core; diff --git a/packages/fullstack/Cargo.toml b/packages/fullstack/Cargo.toml index b58175fb4..44a88254c 100644 --- a/packages/fullstack/Cargo.toml +++ b/packages/fullstack/Cargo.toml @@ -61,25 +61,24 @@ tower-layer = { version = "0.3.2", optional = true } parking_lot = { version = "0.12.1", features = ["send_guard"], optional = true } web-sys = { version = "0.3.61", optional = true, features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } -dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } -clap = { version = "4.5.7", optional = true, features = ["derive"] } +dioxus-cli-config = { workspace = true, optional = true } +dioxus-devtools = { workspace = true, optional = true } aws-lc-rs = { version = "1.8.1", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] tokio = { workspace = true, features = ["rt", "sync"], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -dioxus-hot-reload = { workspace = true, features = ["serve"] } tokio = { workspace = true, features = ["rt", "sync", "rt-multi-thread"], optional = true } [dev-dependencies] dioxus = { workspace = true, features = ["fullstack"] } [features] -default = ["hot-reload", "panic_hook", "document", "file_engine", "mounted"] +default = ["devtools", "panic_hook", "document", "file_engine", "mounted"] panic_hook = ["dioxus-web?/panic_hook"] -hot-reload = ["dioxus-web?/hot_reload", "dioxus-hot-reload/serve"] +devtools = ["dioxus-web?/devtools", "dep:dioxus-devtools"] mounted = ["dioxus-web?/mounted"] file_engine = ["dioxus-web?/file_engine"] document = ["dioxus-web?/document"] @@ -108,8 +107,6 @@ server = [ "dep:async-trait", "dep:parking_lot", "dioxus-interpreter-js", - "dep:clap", - "dioxus-cli-config/read-from-args", ] aws-lc-rs = ["dep:aws-lc-rs"] diff --git a/packages/fullstack/src/launch.rs b/packages/fullstack/src/launch.rs index 5b6672624..e590efc41 100644 --- a/packages/fullstack/src/launch.rs +++ b/packages/fullstack/src/launch.rs @@ -126,16 +126,9 @@ async fn launch_server( build_virtual_dom: impl Fn() -> VirtualDom + Send + Sync + 'static, context_providers: ContextProviders, ) { - use clap::Parser; - // Get the address the server should run on. If the CLI is running, the CLI proxies fullstack into the main address // and we use the generated address the CLI gives us - let cli_args = dioxus_cli_config::RuntimeCLIArguments::from_cli(); - let address = cli_args - .as_ref() - .map(|args| args.fullstack_address()) - .unwrap_or_else(dioxus_cli_config::AddressArguments::parse) - .address(); + let address = dioxus_cli_config::fullstack_address_or_localhost(); #[cfg(feature = "axum")] { diff --git a/packages/hot-reload/Cargo.toml b/packages/hot-reload/Cargo.toml deleted file mode 100644 index b8466a219..000000000 --- a/packages/hot-reload/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "dioxus-hot-reload" -authors = ["Jonathan Kelley", "Evan Almloff"] -version = { workspace = true } -edition = "2021" -license = "MIT OR Apache-2.0" -repository = "https://github.com/DioxusLabs/dioxus/" -homepage = "https://dioxuslabs.com/learn/0.4/migration/hot_reload" -description = "Hot reloading utilities for Dioxus" -keywords = ["dom", "ui", "gui", "react", "hot-reloading"] - -[dependencies] -dioxus-rsx = { workspace = true } -dioxus-rsx-hotreload = { workspace = true } -dioxus-core = { workspace = true, features = ["serialize"] } -dioxus-html = { workspace = true, optional = true } -dioxus-signals = { workspace = true, optional = true } -dioxus-cli-config = { workspace = true, optional = true, features = ["read-config"] } - -notify = { workspace = true, optional = true } -chrono = { version = "0.4.24", default-features = false, features = ["clock"], optional = true } -serde_json = "1.0.91" -serde = { version = "1", features = ["derive"] } -execute = { version = "0.2.11", optional = true } -once_cell = { version = "1.17.0", optional = true } -ignore = { version = "0.4.19", optional = true } - -# hot reloading serve -tokio-stream = { version = "0.1.12", features = ["sync"], optional = true } -futures-util = { workspace = true, features = ["async-await-macro"], optional = true } -tokio = { workspace = true, features = ["sync", "rt-multi-thread"], optional = true } -tracing = { workspace = true } -warnings.workspace = true - -# use rustls on android -[target.'cfg(target_os = "android")'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["rustls"] } - -# use native tls on other platforms -[target.'cfg(not(target_os = "android"))'.dependencies] -tokio-tungstenite = { workspace = true, optional = true, features = ["native-tls"] } - -[dev-dependencies] -tokio = { workspace = true, features = ["full"] } - -[features] -default = ["dioxus-html"] -client = ["dep:dioxus-signals"] -serve = ["dep:tokio-stream", "dep:futures-util", "dep:tokio", "dep:tokio-tungstenite", "dep:dioxus-cli-config"] - -[package.metadata.docs.rs] -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"] diff --git a/packages/hot-reload/README.md b/packages/hot-reload/README.md deleted file mode 100644 index 5c5d3fbf3..000000000 --- a/packages/hot-reload/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# `dioxus-hot-reload`: Hot Reloading Utilities for Dioxus - -[![Crates.io][crates-badge]][crates-url] -[![MIT licensed][mit-badge]][mit-url] -[![Build Status][actions-badge]][actions-url] -[![Discord chat][discord-badge]][discord-url] - -[crates-badge]: https://img.shields.io/crates/v/dioxus-hot-reload.svg -[crates-url]: https://crates.io/crates/dioxus-hot-reload -[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg -[mit-url]: https://github.com/dioxuslabs/dioxus/blob/main/LICENSE-MIT -[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg -[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster -[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square -[discord-url]: https://discord.gg/XgGxMSkvUM - -[Website](https://dioxuslabs.com) | -[Guides](https://dioxuslabs.com/learn/0.5/) | -[API Docs](https://docs.rs/dioxus-hot-reload/latest/dioxus_hot_reload) | -[Chat](https://discord.gg/XgGxMSkvUM) - -## Overview - -Dioxus supports hot reloading for static parts of rsx macros. This enables changing the styling of your application without recompiling the rust code. This is useful for rapid iteration on the styling of your application. - -Hot reloading could update the following change without recompiling: - -```rust -rsx! { - div { - "Count: {count}", - } -} -``` - -=> - -```rust -rsx! { - div { - color: "red", - font_size: "2em", - "Count: {count}", - } -} -``` - -But it could not update the following change: - -```rust -rsx! { - div { - "Count: {count}", - } -} -``` - -=> - -```rust -rsx! { - div { - "Count: {count*2}", - onclick: |_| println!("clicked"), - } -} -``` - -## Usage - -> This crate implements hot reloading for native compilation targets not WASM. For hot reloading with the web renderer, see the [dioxus-cli](https://github.com/DioxusLabs/dioxus/tree/master/packages/cli) project. - -Add this to the top of your main function on any renderer that supports hot reloading to start the hot reloading server: - -```rust -fn main(){ - hot_reload_init!(); - // launch your application -} -``` - -By default the dev server watches on the root of the crate the macro is called in and ignores changes in the `/target` directory and any directories set in the `.gitignore` file in the root directory. To watch on custom paths pass call the `with_paths` function on the config builder: - -```rust -fn main(){ - hot_reload_init!(Config::new().with_paths(&["src", "examples", "assets"])); - // launch your application -} -``` - -By default the hot reloading server will output some logs in the console, to disable these logs call the `with_logging` function on the config builder: - -```rust -fn main(){ - hot_reload_init!(Config::new().with_logging(false)); - // launch your application -} -``` - -To rebuild the application when the logic changes, you can use the `with_rebuild_command` function on the config builder. This command will be called when hot reloading fails to quickly update the rsx: - -```rust -fn main(){ - hot_reload_init!(Config::new().with_rebuild_command("cargo run")); - // launch your application -} -``` - -If you are using a namespace other than html, you can implement the [HotReloadingContext](https://docs.rs/dioxus-rsx/latest/dioxus_rsx/index.html#reexport.HotReloadingContext) trait to provide a mapping between the rust names of your elements/attributes and the resulting strings. - -You can then provide the Context to the builder to make hot reloading work with your custom namespace: - -```rust -fn main(){ - // Note: Use default instead of new if you are using a custom namespace - hot_reload_init!(Config::::default()); - // launch your application -} -``` - -## Implementing Hot Reloading for a Custom Renderer - -To add hot reloading support to your custom renderer you can use the connect function. This will connect to the dev server you just need to provide a way to transfer `Template`s to the `VirtualDom`. Once you implement this your users can use the hot_reload_init function just like any other render. - -```rust -async fn launch(app: Component) { - let mut vdom = VirtualDom::new(app); - // ... - - let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); - dioxus_hot_reload::connect(move |msg| { - let _ = tx.send(msg); - }); - - loop { - tokio::select! { - Some(msg) = rx.recv() => { - match msg{ - HotReloadMsg::Shutdown => { - // ... shutdown the application - } - HotReloadMsg::UpdateTemplate(template) => { - // update the template in the virtual dom - vdom.replace_template(template); - } - } - } - _ = vdom.wait_for_work() => { - // ... - } - } - let mutations = vdom.render_immediate(); - // apply the mutations to the dom - } -} -``` - -## Contributing - -- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues). -- Join the discord and ask questions! - -## License - -This project is licensed under the [MIT license]. - -[mit license]: https://github.com/dioxuslabs/dioxus/blob/main/LICENSE-MIT - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in Dioxus by you shall be licensed as MIT without any additional -terms or conditions. diff --git a/packages/hot-reload/src/client.rs b/packages/hot-reload/src/client.rs deleted file mode 100644 index ca825e517..000000000 --- a/packages/hot-reload/src/client.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::HotReloadMsg; -use dioxus_core::{ScopeId, VirtualDom}; -use dioxus_signals::Writable; -use warnings::Warning; - -/// Applies template and literal changes to the VirtualDom -/// -/// Assets need to be handled by the renderer. -pub fn apply_changes(dom: &mut VirtualDom, msg: &HotReloadMsg) { - dom.runtime().on_scope(ScopeId::ROOT, || { - let ctx = dioxus_signals::get_global_context(); - - for template in &msg.templates { - let id = &template.location; - let value = template.template.clone(); - if let Some(mut signal) = ctx.get_signal_with_key(id) { - dioxus_signals::warnings::signal_read_and_write_in_reactive_scope::allow(|| { - dioxus_signals::warnings::signal_write_in_component_body::allow(|| { - signal.set(Some(value)); - }); - }); - } - } - }); -} diff --git a/packages/hot-reload/src/ws_receiver.rs b/packages/hot-reload/src/ws_receiver.rs deleted file mode 100644 index b9346fd81..000000000 --- a/packages/hot-reload/src/ws_receiver.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::DevserverMsg; -use futures_util::{SinkExt, StreamExt}; -use tokio::net::TcpStream; -use tokio_tungstenite::{ - tungstenite::{Message, Result as TtResult}, - MaybeTlsStream, WebSocketStream, -}; - -pub fn connect(mut callback: impl FnMut(DevserverMsg) + Send + 'static) { - tokio::spawn(async move { - let Some(Ok(mut recv)) = NativeReceiver::create_from_cli().await else { - return; - }; - while let Some(msg) = recv.next().await { - match msg { - Ok(msg) => callback(msg), - Err(_e) => {} - } - } - }); -} - -/// A receiver for messages from the devserver -/// -/// Calling `next` will watch the channel for the next valid message from the devserver -pub struct NativeReceiver { - socket: WebSocketStream>, -} - -impl NativeReceiver { - /// Connect to the devserver - async fn create(url: String) -> TtResult { - let (socket, _ws) = tokio_tungstenite::connect_async(&url).await?; - Ok(Self { socket }) - } - - /// Connect to the devserver with an address from the CLI. Returns None if the current application was not run with the CLI - pub async fn create_from_cli() -> Option> { - let cli_args = dioxus_cli_config::RuntimeCLIArguments::from_cli()?; - let addr = cli_args.cli_address(); - Some(Self::create(format!("ws://{addr}/_dioxus")).await) - } - - /// Wait for the next message from the devserver - /// - /// Returns None when the connection is closed or socket.next() returns None - pub async fn next(&mut self) -> Option> { - loop { - let res = self.socket.next().await?; - - match res { - Ok(res) => match res { - Message::Text(text) => { - // let leaked: &'static str = Box::leak(text.into_boxed_str()); - let msg = serde_json::from_str::(&text); - if let Ok(msg) = msg { - return Some(Ok(msg)); - } - } - // send a pong - Message::Ping(_) => { - let _ = self.socket.send(Message::Pong(vec![])).await; - } - Message::Close(_) => return None, - Message::Binary(_) => {} - Message::Pong(_) => {} - Message::Frame(_) => {} - }, - Err(e) => return Some(Err(e)), - }; - } - } -} diff --git a/packages/liveview/Cargo.toml b/packages/liveview/Cargo.toml index e2e81e147..865944395 100644 --- a/packages/liveview/Cargo.toml +++ b/packages/liveview/Cargo.toml @@ -26,8 +26,8 @@ dioxus-html = { workspace = true, features = ["serialize", "document", "mounted" rustc-hash = { workspace = true } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-interpreter-js = { workspace = true, features = ["binary-protocol"] } -dioxus-hot-reload = { workspace = true, optional = true, features = ["serve", "client"] } -dioxus-cli-config = { workspace = true, features = ["read-config", "read-from-args"] } +dioxus-devtools = { workspace = true, optional = true } +dioxus-cli-config = { workspace = true } generational-box = { workspace = true } # axum @@ -41,10 +41,10 @@ tower = { workspace = true } dioxus = { workspace = true } [features] -default = ["hot-reload", "multi-thread"] +default = ["devtools", "multi-thread"] axum = ["dep:axum"] multi-thread = ["tokio/rt-multi-thread"] -hot-reload = ["dep:dioxus-hot-reload"] +devtools = ["dep:dioxus-devtools"] [[example]] name = "axum" diff --git a/packages/liveview/src/config.rs b/packages/liveview/src/config.rs index 49194d692..0f22eba8b 100644 --- a/packages/liveview/src/config.rs +++ b/packages/liveview/src/config.rs @@ -1,13 +1,9 @@ -use dioxus_cli_config::{RuntimeCLIArguments, CURRENT_CONFIG}; use dioxus_core::VirtualDom; use crate::LiveviewRouter; pub(crate) fn app_title() -> String { - CURRENT_CONFIG - .as_ref() - .map(|c| c.web.app.title.clone()) - .unwrap_or_else(|_| "Dioxus Liveview App".to_string()) + dioxus_cli_config::app_title().unwrap_or_else(|| "Dioxus Liveview App".to_string()) } /// A configuration for the LiveView server. @@ -19,15 +15,9 @@ pub struct Config { impl Default for Config { fn default() -> Self { - let address = RuntimeCLIArguments::from_cli() - .map(|args| args.fullstack_address().address()) - .unwrap_or(std::net::SocketAddr::V4(std::net::SocketAddrV4::new( - std::net::Ipv4Addr::new(127, 0, 0, 1), - 8080, - ))); Self { + address: dioxus_cli_config::fullstack_address_or_localhost(), router: R::create_default_liveview_router(), - address, route: "/".to_string(), } } diff --git a/packages/liveview/src/pool.rs b/packages/liveview/src/pool.rs index 2f15de86b..dc5f7ad41 100644 --- a/packages/liveview/src/pool.rs +++ b/packages/liveview/src/pool.rs @@ -6,7 +6,6 @@ use crate::{ LiveViewError, }; use dioxus_core::prelude::*; -use dioxus_hot_reload::DevserverMsg; use dioxus_html::{EventData, HtmlEvent, PlatformEventData}; use dioxus_interpreter_js::MutationState; use futures_util::{pin_mut, SinkExt, StreamExt}; @@ -117,10 +116,12 @@ impl LiveViewSocket for S where /// /// You might need to transform the error types of the web backend into the LiveView error type. pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), LiveViewError> { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let mut hot_reload_rx = { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); - dioxus_hot_reload::connect(move |template| _ = tx.send(template)); + if let Some(endpoint) = dioxus_cli_config::devserver_ws_endpoint() { + dioxus_devtools::connect(endpoint, move |template| _ = tx.send(template)); + } rx }; @@ -157,9 +158,9 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } loop { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let hot_reload_wait = hot_reload_rx.recv(); - #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] let hot_reload_wait: std::future::Pending> = std::future::pending(); tokio::select! { @@ -213,22 +214,22 @@ pub async fn run(mut vdom: VirtualDom, ws: impl LiveViewSocket) -> Result<(), Li } Some(msg) = hot_reload_wait => { - #[cfg(all(feature = "hot-reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] match msg{ - DevserverMsg::HotReload(msg)=> { - dioxus_hot_reload::apply_changes(&mut vdom, &msg); + dioxus_devtools::DevserverMsg::HotReload(msg)=> { + dioxus_devtools::apply_changes(&vdom, &msg); } - DevserverMsg::Shutdown => { + dioxus_devtools::DevserverMsg::Shutdown => { std::process::exit(0); }, - DevserverMsg::FullReloadCommand - | DevserverMsg::FullReloadStart - | DevserverMsg::FullReloadFailed => { + dioxus_devtools::DevserverMsg::FullReloadCommand + | dioxus_devtools::DevserverMsg::FullReloadStart + | dioxus_devtools::DevserverMsg::FullReloadFailed => { // usually only web gets this message - what are we supposed to do? // Maybe we could just binary patch ourselves in place without losing window state? }, } - #[cfg(not(all(feature = "hot-reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] let () = msg; } } diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 89fca32bf..2cbe5d6cf 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -29,7 +29,7 @@ dioxus-ssr = { workspace = true, optional = true } http = { workspace = true, optional = true } dioxus-fullstack = { workspace = true, optional = true } tokio = { workspace = true, features = ["full"], optional = true } -dioxus-cli-config = { workspace = true, features = ["read-config"] } +dioxus-cli-config = { workspace = true } rustversion = "1.0.17" # you need to comment this out when publishing since cargo workspaces is not smart enough to wipe this when dropping diff --git a/packages/router/src/history/web.rs b/packages/router/src/history/web.rs index 0f6811da6..443e7feab 100644 --- a/packages/router/src/history/web.rs +++ b/packages/router/src/history/web.rs @@ -1,4 +1,7 @@ -use std::sync::{Arc, Mutex}; +use std::{ + path::PathBuf, + sync::{Arc, Mutex}, +}; use gloo::{console::error, events::EventListener, render::AnimationFrame}; @@ -14,12 +17,12 @@ use super::{ }; #[allow(dead_code)] -fn base_path() -> Option<&'static str> { +fn base_path() -> Option { tracing::trace!( - "Using base_path from Dioxus.toml: {:?}", - dioxus_cli_config::BASE_PATH + "Using base_path from the CLI: {:?}", + dioxus_cli_config::base_path() ); - dioxus_cli_config::BASE_PATH + dioxus_cli_config::base_path() } #[allow(clippy::extra_unused_type_parameters)] @@ -97,7 +100,7 @@ impl WebHistory { let prefix = prefix // If there isn't a base path, try to grab one from the CLI - .or_else(|| base_path().map(|s| s.to_string())) + .or_else(|| base_path().map(|s| s.display().to_string())) // Normalize the prefix to start and end with no slashes .map(|prefix| prefix.trim_matches('/').to_string()) // If the prefix is empty, don't add it diff --git a/packages/rsx/.vscode/settings.json b/packages/rsx/.vscode/settings.json index 693f5b782..631102928 100644 --- a/packages/rsx/.vscode/settings.json +++ b/packages/rsx/.vscode/settings.json @@ -1,8 +1,4 @@ { "rust-analyzer.check.workspace": false, - // "rust-analyzer.check.extraArgs": [ - // "--features", - // "hot_reload" - // ], "rust-analyzer.cargo.features": "all" } diff --git a/packages/static-generation/Cargo.toml b/packages/static-generation/Cargo.toml index cd4116f47..3e5ac6e32 100644 --- a/packages/static-generation/Cargo.toml +++ b/packages/static-generation/Cargo.toml @@ -18,8 +18,8 @@ dioxus-ssr = { workspace = true, optional = true } dioxus-isrg = { workspace = true, optional = true } axum = { workspace = true, features = ["ws", "macros"], optional = true } tower-http = { workspace = true, features = ["fs"], optional = true } -dioxus-hot-reload = { workspace = true, features = ["serve"], optional = true } -dioxus-cli-config = { workspace = true, features = ["read-config"], optional = true } +dioxus-devtools = { workspace = true, optional = true } +dioxus-cli-config = { workspace = true, optional = true } dioxus-web = { workspace = true, features = ["hydrate"], optional = true } tokio = { workspace = true, optional = true } http = { workspace = true, optional = true } @@ -32,7 +32,7 @@ criterion = { workspace = true } [features] default = [] -server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-hot-reload", "dep:dioxus-cli-config", "dep:tower", "dep:dioxus-isrg"] +server = ["dioxus-fullstack/server", "dioxus-router/ssr", "dep:dioxus-ssr", "dep:tokio", "dep:http", "dep:axum", "dep:tower-http", "dep:dioxus-devtools", "dep:dioxus-cli-config", "dep:tower", "dep:dioxus-isrg"] web = ["dioxus-fullstack/web", "dioxus-router/web", "dep:dioxus-web"] [package.metadata.docs.rs] diff --git a/packages/static-generation/src/launch.rs b/packages/static-generation/src/launch.rs index 30aceae7a..a5a7297ef 100644 --- a/packages/static-generation/src/launch.rs +++ b/packages/static-generation/src/launch.rs @@ -41,19 +41,12 @@ pub fn launch( .unwrap(); // Serve the program if we are running with cargo - if std::env::var_os("CARGO").is_some() || std::env::var_os("DIOXUS_ACTIVE").is_some() { + if std::env::var_os("CARGO").is_some() || dioxus_cli_config::is_cli_enabled() { // Get the address the server should run on. If the CLI is running, the CLI proxies static generation into the main address // and we use the generated address the CLI gives us - let cli_args = dioxus_cli_config::RuntimeCLIArguments::from_cli(); - let address = cli_args - .as_ref() - .map(|args| args.fullstack_address().address()) - .unwrap_or_else(|| std::net::SocketAddr::from(([127, 0, 0, 1], 8080))); + let serve_address = dioxus_cli_config::fullstack_address_or_localhost(); // Point the user to the CLI address if the CLI is running or the fullstack address if not - let serve_address = cli_args - .map(|args| args.cli_address()) - .unwrap_or_else(|| address); println!( "Serving static files from {} at http://{serve_address}", path.display() @@ -77,7 +70,7 @@ pub fn launch( }))) }; - let listener = tokio::net::TcpListener::bind(address).await.unwrap(); + let listener = tokio::net::TcpListener::bind(serve_address).await.unwrap(); axum::serve(listener, router.into_make_service()) .await .unwrap(); diff --git a/packages/static-generation/src/ssg.rs b/packages/static-generation/src/ssg.rs index 0c58c0d8d..a4cae763c 100644 --- a/packages/static-generation/src/ssg.rs +++ b/packages/static-generation/src/ssg.rs @@ -65,10 +65,7 @@ pub async fn generate_static_site( } // Copy over the web output dir into the static output dir - let assets_path = dioxus_cli_config::CURRENT_CONFIG - .as_ref() - .map(|c| c.application.out_dir.clone()) - .unwrap_or("./dist".into()); + let assets_path = dioxus_cli_config::out_dir().unwrap_or("./dist".into()); let assets_path = assets_path.join("public"); diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 8c0d3b429..21858ef66 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["dom", "ui", "gui", "react", "wasm"] dioxus-core = { workspace = true } dioxus-core-types = { workspace = true } dioxus-html = { workspace = true, features = ["wasm-bind"] } -dioxus-hot-reload = { workspace = true, features = ["client"] } +dioxus-devtools = { workspace = true } dioxus-signals = { workspace = true } dioxus-interpreter-js = { workspace = true, features = [ "minimal_bindings", @@ -59,14 +59,14 @@ features = [ ] [features] -default = ["panic_hook", "mounted", "file_engine", "hot_reload", "document"] +default = ["panic_hook", "mounted", "file_engine", "devtools", "document"] panic_hook = ["dep:console_error_panic_hook"] hydrate = ["web-sys/Comment", "ciborium", "dep:serde"] mounted = ["web-sys/Element", "dioxus-html/mounted"] file_engine = [ "dioxus-html/file-engine", ] -hot_reload = ["web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", "dep:serde_json", "dep:serde", "dioxus-core/serialize"] +devtools = ["web-sys/MessageEvent", "web-sys/WebSocket", "web-sys/Location", "dep:serde_json", "dep:serde", "dioxus-core/serialize"] document = ["dioxus-html/document", "dep:serde-wasm-bindgen", "dep:serde_json", "dep:serde"] [dev-dependencies] diff --git a/packages/web/src/hot_reload.rs b/packages/web/src/devtools.rs similarity index 99% rename from packages/web/src/hot_reload.rs rename to packages/web/src/devtools.rs index b0cdb0b91..48a6f8ea0 100644 --- a/packages/web/src/hot_reload.rs +++ b/packages/web/src/devtools.rs @@ -7,7 +7,7 @@ use std::fmt::Display; use std::time::Duration; use dioxus_core::ScopeId; -use dioxus_hot_reload::{DevserverMsg, HotReloadMsg}; +use dioxus_devtools::{DevserverMsg, HotReloadMsg}; use dioxus_html::prelude::eval; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use js_sys::JsString; diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs index 44f7b94c5..4600ddb6c 100644 --- a/packages/web/src/lib.rs +++ b/packages/web/src/lib.rs @@ -39,8 +39,8 @@ mod document; #[cfg(feature = "document")] pub use document::WebDocument; -#[cfg(all(feature = "hot_reload", debug_assertions))] -mod hot_reload; +#[cfg(all(feature = "devtools", debug_assertions))] +mod devtools; mod hydration; #[allow(unused)] @@ -67,8 +67,8 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { console_error_panic_hook::set_once(); } - #[cfg(all(feature = "hot_reload", debug_assertions))] - let mut hotreload_rx = hot_reload::init(); + #[cfg(all(feature = "devtools", debug_assertions))] + let mut hotreload_rx = devtools::init(); let runtime = virtual_dom.runtime(); @@ -123,7 +123,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { loop { // if virtual dom has nothing, wait for it to have something before requesting idle time // if there is work then this future resolves immediately. - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] let template; #[allow(unused)] let mut hydration_work: Option = None; @@ -137,15 +137,15 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { .flatten(); let mut rx_hydration = hydration_receiver_iter.select_next_some(); - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] #[allow(unused)] { - let mut hot_reload_next = hotreload_rx.select_next_some(); + let mut devtools_next = hotreload_rx.select_next_some(); select! { _ = work => { template = None; }, - new_template = hot_reload_next => { + new_template = devtools_next => { template = Some(new_template); }, hydration_data = rx_hydration => { @@ -158,7 +158,7 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { } } - #[cfg(not(all(feature = "hot_reload", debug_assertions)))] + #[cfg(not(all(feature = "devtools", debug_assertions)))] #[allow(unused)] { select! { @@ -173,13 +173,13 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! { } } - #[cfg(all(feature = "hot_reload", debug_assertions))] + #[cfg(all(feature = "devtools", debug_assertions))] if let Some(hr_msg) = template { // Replace all templates - dioxus_hot_reload::apply_changes(&mut virtual_dom, &hr_msg); + dioxus_devtools::apply_changes(&virtual_dom, &hr_msg); if !hr_msg.assets.is_empty() { - crate::hot_reload::invalidate_browser_asset_cache(); + crate::devtools::invalidate_browser_asset_cache(); } }