Merge pull request #1369 from ealmloff/intigrate-collect-assets

Unify Collecting and Optimizing Assets
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-01-05 12:13:37 -08:00
commit 8f6cdd88b2
50 files changed
+824 -145

No files matched your search

+2
View File
@@ -36,6 +36,8 @@ jobs:
toolchain: ${{ matrix.platform.toolchain }} toolchain: ${{ matrix.platform.toolchain }}
targets: ${{ matrix.platform.target }} targets: ${{ matrix.platform.target }}
- uses: ilammy/setup-nasm@v1
# Setup the Github Actions Cache for the CLI package # Setup the Github Actions Cache for the CLI package
- name: Setup cache - name: Setup cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
+6
View File
@@ -39,6 +39,7 @@ jobs:
steps: steps:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- uses: ilammy/setup-nasm@v1
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -51,6 +52,7 @@ jobs:
steps: steps:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- uses: ilammy/setup-nasm@v1
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev
- uses: davidB/rust-cargo-make@v1 - uses: davidB/rust-cargo-make@v1
@@ -66,6 +68,7 @@ jobs:
steps: steps:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- uses: ilammy/setup-nasm@v1
- run: rustup component add rustfmt - run: rustup component add rustfmt
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: cargo fmt --all -- --check - run: cargo fmt --all -- --check
@@ -77,6 +80,7 @@ jobs:
steps: steps:
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- uses: ilammy/setup-nasm@v1
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev - run: sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev
- run: rustup component add clippy - run: rustup component add clippy
@@ -124,6 +128,8 @@ jobs:
} }
steps: steps:
- uses: actions/checkout@v4
- uses: ilammy/setup-nasm@v1
- name: install stable - name: install stable
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
+3 -2
View File
@@ -26,8 +26,8 @@ env:
RUST_BACKTRACE: 1 RUST_BACKTRACE: 1
# Change to specific Rust release to pin # Change to specific Rust release to pin
rust_stable: stable rust_stable: stable
rust_nightly: nightly-2022-11-03 rust_nightly: nightly-2023-11-16
rust_clippy: 1.65.0 rust_clippy: 1.70.0
# When updating this, also update: # When updating this, also update:
# - README.md # - README.md
# - tokio/README.md # - tokio/README.md
@@ -70,6 +70,7 @@ jobs:
run: echo "MIRIFLAGS=-Zmiri-tag-gc=1" >> $GITHUB_ENV run: echo "MIRIFLAGS=-Zmiri-tag-gc=1" >> $GITHUB_ENV
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ilammy/setup-nasm@v1
- name: Install Rust ${{ env.rust_nightly }} - name: Install Rust ${{ env.rust_nightly }}
uses: dtolnay/rust-toolchain@master uses: dtolnay/rust-toolchain@master
with: with:
+1
View File
@@ -20,6 +20,7 @@ jobs:
steps: steps:
# Do our best to cache the toolchain and node install steps # Do our best to cache the toolchain and node install steps
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ilammy/setup-nasm@v1
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: 16 node-version: 16
+1 -1
View File
@@ -61,7 +61,7 @@ fn compose(cx: Scope<ComposeProps>) -> Element {
}, },
"Click to send" "Click to send"
} }
input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" } input { oninput: move |e| user_input.set(e.value()), value: "{user_input}" }
} }
}) })
+1 -1
View File
@@ -10,7 +10,7 @@ fn app(cx: Scope) -> Element {
p { p {
"This should show an image:" "This should show an image:"
} }
img { src: "examples/assets/logo.png" } img { src: mg!(image("examples/assets/logo.png").format(ImageType::Avif)).to_string() }
} }
}) })
} }
+2 -1
View File
@@ -18,13 +18,14 @@ fn main() {
); );
} }
const _STYLE: &str = mg!(file("./examples/assets/fileexplorer.css"));
fn app(cx: Scope) -> Element { fn app(cx: Scope) -> Element {
let files = use_ref(cx, Files::new); let files = use_ref(cx, Files::new);
cx.render(rsx! { cx.render(rsx! {
div { div {
link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", } link { href:"https://fonts.googleapis.com/icon?family=Material+Icons", rel:"stylesheet", }
style { include_str!("./assets/fileexplorer.css") }
header { header {
i { class: "material-icons icon-menu", "menu" } i { class: "material-icons icon-menu", "menu" }
h1 { "Files: ", files.read().current() } h1 { "Files: ", files.read().current() }
+1 -1
View File
@@ -30,7 +30,7 @@ watch_path = ["src", "public"]
[web.resource] [web.resource]
# CSS style file # CSS style file
style = ["/tailwind.css"] style = []
# Javascript code file # Javascript code file
script = [] script = []
@@ -0,0 +1 @@
*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-feature-settings:normal;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{color:inherit;border-top-width:1px;height:0}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.container{width:100%}@media (width>=640px){.container{max-width:640px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1536px){.container{max-width:1536px}}.mx-auto{margin-left:auto;margin-right:auto}.mb-16{margin-bottom:4rem}.mb-4{margin-bottom:1rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-5{margin-right:1.25rem}.mt-4{margin-top:1rem}.flex{display:flex}.inline-flex{display:inline-flex}.hidden{display:none}.h-10{height:2.5rem}.h-4{height:1rem}.w-10{width:2.5rem}.w-4{width:1rem}.w-5\/6{width:83.3333%}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-center{justify-content:center}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.border-0{border-width:0}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity))}.object-cover{-o-object-fit:cover;object-fit:cover}.object-center{-o-object-position:center;object-position:center}.p-2{padding:.5rem}.p-5{padding:1.25rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-24{padding-top:6rem;padding-bottom:6rem}.text-center{text-align:center}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-medium{font-weight:500}.leading-relaxed{line-heiLine truncated
+3 -5
View File
@@ -2,13 +2,11 @@
use dioxus::prelude::*; use dioxus::prelude::*;
const _STYLE: &str = mg!(file("./public/tailwind.css"));
fn main() { fn main() {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
dioxus_desktop::launch_cfg( dioxus_desktop::launch(app);
app,
dioxus_desktop::Config::new()
.with_custom_head(r#"<link rel="stylesheet" href="public/tailwind.css">"#.to_string()),
);
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
dioxus_web::launch(app); dioxus_web::launch(app);
} }
+2 -1
View File
@@ -7,6 +7,8 @@ fn main() {
dioxus_desktop::launch(app); dioxus_desktop::launch(app);
} }
const _STYLE: &str = mg!(file("./examples/assets/todomvc.css"));
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
pub enum FilterState { pub enum FilterState {
All, All,
@@ -47,7 +49,6 @@ pub fn app(cx: Scope<()>) -> Element {
cx.render(rsx! { cx.render(rsx! {
section { class: "todoapp", section { class: "todoapp",
style { include_str!("./assets/todomvc.css") }
TodoHeader { todos: todos } TodoHeader { todos: todos }
section { class: "main", section { class: "main",
if !todos.is_empty() { if !todos.is_empty() {
+3 -1
View File
@@ -30,7 +30,7 @@ cargo_metadata = "0.15.0"
tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros"] } tokio = { version = "1.16.1", features = ["fs", "sync", "rt", "macros"] }
atty = "0.2.14" atty = "0.2.14"
chrono = "0.4.19" chrono = "0.4.19"
anyhow = "1.0.53" anyhow = "1"
hyper = "0.14.17" hyper = "0.14.17"
hyper-rustls = "0.23.2" hyper-rustls = "0.23.2"
indicatif = "0.17.5" indicatif = "0.17.5"
@@ -75,6 +75,8 @@ toml_edit = "0.19.11"
tauri-bundler = { version = "=1.3.0", features = ["native-tls-vendored"] } tauri-bundler = { version = "=1.3.0", features = ["native-tls-vendored"] }
tauri-utils = "=1.4.*" tauri-utils = "=1.4.*"
manganis-cli-support= { git = "https://github.com/DioxusLabs/collect-assets", features = ["webp", "html"] }
dioxus-autofmt = { workspace = true } dioxus-autofmt = { workspace = true }
dioxus-check = { workspace = true } dioxus-check = { workspace = true }
rsx-rosetta = { workspace = true } rsx-rosetta = { workspace = true }
+93 -22
View File
@@ -2,31 +2,35 @@ use crate::{
config::{CrateConfig, ExecutableType}, config::{CrateConfig, ExecutableType},
error::{Error, Result}, error::{Error, Result},
tools::Tool, tools::Tool,
DioxusConfig,
}; };
use cargo_metadata::{diagnostic::Diagnostic, Message}; use cargo_metadata::{diagnostic::Diagnostic, Message};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use manganis_cli_support::AssetManifestExt;
use serde::Serialize; use serde::Serialize;
use std::{ use std::{
fs::{copy, create_dir_all, File}, fs::{copy, create_dir_all, File},
io::Read, io::{Read, Write},
panic, panic,
path::PathBuf, path::PathBuf,
time::Duration, time::Duration,
}; };
use wasm_bindgen_cli_support::Bindgen; use wasm_bindgen_cli_support::Bindgen;
lazy_static! {
static ref PROGRESS_BARS: indicatif::MultiProgress = indicatif::MultiProgress::new();
}
#[derive(Serialize, Debug, Clone)] #[derive(Serialize, Debug, Clone)]
pub struct BuildResult { pub struct BuildResult {
pub warnings: Vec<Diagnostic>, pub warnings: Vec<Diagnostic>,
pub elapsed_time: u128, pub elapsed_time: u128,
} }
#[allow(unused)] pub fn build(config: &CrateConfig, _: bool, skip_assets: bool) -> Result<BuildResult> {
pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
// [1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?) // [1] Build the project with cargo, generating a wasm32-unknown-unknown target (is there a more specific, better target to leverage?)
// [2] Generate the appropriate build folders // [2] Generate the appropriate build folders
// [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm // [3] Wasm-bindgen the .wasm file, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm
// [4] Wasm-opt the .wasm file with whatever optimizations need to be done // [4] Wasm-opt the .wasm file with whatever optimizations need to be done
// [5][OPTIONAL] Builds the Tailwind CSS file using the Tailwind standalone binary // [5][OPTIONAL] Builds the Tailwind CSS file using the Tailwind standalone binary
// [6] Link up the html page to the wasm module // [6] Link up the html page to the wasm module
@@ -41,6 +45,8 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
.. ..
} = config; } = config;
let _gaurd = WebAssetConfigDropGuard::new();
// start to build the assets // start to build the assets
let ignore_files = build_assets(config)?; let ignore_files = build_assets(config)?;
@@ -60,8 +66,8 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
.output()?; .output()?;
} }
let cmd = subprocess::Exec::cmd("cargo"); let cmd = subprocess::Exec::cmd("cargo")
let cmd = cmd .env("CARGO_TARGET_DIR", target_dir)
.cwd(crate_dir) .cwd(crate_dir)
.arg("build") .arg("build")
.arg("--target") .arg("--target")
@@ -252,19 +258,28 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
} }
} }
if !skip_assets {
process_assets(config)?;
}
Ok(BuildResult { Ok(BuildResult {
warnings: warning_messages, warnings: warning_messages,
elapsed_time: t_start.elapsed().as_millis(), elapsed_time: t_start.elapsed().as_millis(),
}) })
} }
pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResult> { pub fn build_desktop(
config: &CrateConfig,
_is_serve: bool,
skip_assets: bool,
) -> Result<BuildResult> {
log::info!("🚅 Running build [Desktop] command..."); log::info!("🚅 Running build [Desktop] command...");
let t_start = std::time::Instant::now(); let t_start = std::time::Instant::now();
let ignore_files = build_assets(config)?; let ignore_files = build_assets(config)?;
let mut cmd = subprocess::Exec::cmd("cargo") let mut cmd = subprocess::Exec::cmd("cargo")
.env("CARGO_TARGET_DIR", &config.target_dir)
.cwd(&config.crate_dir) .cwd(&config.crate_dir)
.arg("build") .arg("build")
.arg("--message-format=json"); .arg("--message-format=json");
@@ -375,6 +390,13 @@ pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResul
} }
} }
if !skip_assets {
// Collect assets
process_assets(config)?;
// Create the __assets_head.html file for bundling
create_assets_head(config)?;
}
log::info!( log::info!(
"🚩 Build completed: [./{}]", "🚩 Build completed: [./{}]",
config config
@@ -394,11 +416,19 @@ pub fn build_desktop(config: &CrateConfig, _is_serve: bool) -> Result<BuildResul
}) })
} }
fn create_assets_head(config: &CrateConfig) -> Result<()> {
let manifest = config.asset_manifest();
let mut file = File::create(config.out_dir.join("__assets_head.html"))?;
file.write_all(manifest.head().as_bytes())?;
Ok(())
}
fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> { fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> {
let mut warning_messages: Vec<Diagnostic> = vec![]; let mut warning_messages: Vec<Diagnostic> = vec![];
let pb = ProgressBar::new_spinner(); let mut pb = ProgressBar::new_spinner();
pb.enable_steady_tick(Duration::from_millis(200)); pb.enable_steady_tick(Duration::from_millis(200));
pb = PROGRESS_BARS.add(pb);
pb.set_style( pb.set_style(
ProgressStyle::with_template("{spinner:.dim.bold} {wide_msg}") ProgressStyle::with_template("{spinner:.dim.bold} {wide_msg}")
.unwrap() .unwrap()
@@ -406,14 +436,6 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> {
); );
pb.set_message("💼 Waiting to start build the project..."); pb.set_message("💼 Waiting to start build the project...");
struct StopSpinOnDrop(ProgressBar);
impl Drop for StopSpinOnDrop {
fn drop(&mut self) {
self.0.finish_and_clear();
}
}
let stdout = cmd.detached().stream_stdout()?; let stdout = cmd.detached().stream_stdout()?;
let reader = std::io::BufReader::new(stdout); let reader = std::io::BufReader::new(stdout);
@@ -449,13 +471,17 @@ fn prettier_build(cmd: subprocess::Exec) -> anyhow::Result<Vec<Diagnostic>> {
std::process::exit(1); std::process::exit(1);
} }
} }
_ => (), // Unknown message _ => {
// Unknown message
}
} }
} }
Ok(warning_messages) Ok(warning_messages)
} }
pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { pub fn gen_page(config: &CrateConfig, serve: bool, skip_assets: bool) -> String {
let _gaurd = WebAssetConfigDropGuard::new();
let crate_root = crate::cargo::crate_root().unwrap(); let crate_root = crate::cargo::crate_root().unwrap();
let custom_html_file = crate_root.join("index.html"); let custom_html_file = crate_root.join("index.html");
let mut html = if custom_html_file.is_file() { let mut html = if custom_html_file.is_file() {
@@ -470,7 +496,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
String::from(include_str!("./assets/index.html")) String::from(include_str!("./assets/index.html"))
}; };
let resources = config.web.resource.clone(); let resources = config.dioxus_config.web.resource.clone();
let mut style_list = resources.style.unwrap_or_default(); let mut style_list = resources.style.unwrap_or_default();
let mut script_list = resources.script.unwrap_or_default(); let mut script_list = resources.script.unwrap_or_default();
@@ -490,6 +516,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
)) ))
} }
if config if config
.dioxus_config
.application .application
.tools .tools
.clone() .clone()
@@ -498,6 +525,10 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
{ {
style_str.push_str("<link rel=\"stylesheet\" href=\"/{base_path}/tailwind.css\">\n"); style_str.push_str("<link rel=\"stylesheet\" href=\"/{base_path}/tailwind.css\">\n");
} }
if !skip_assets {
let manifest = config.asset_manifest();
style_str.push_str(&manifest.head());
}
replace_or_insert_before("{style_include}", &style_str, "</head", &mut html); replace_or_insert_before("{style_include}", &style_str, "</head", &mut html);
@@ -518,11 +549,11 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
); );
} }
let base_path = match &config.web.app.base_path { let base_path = match &config.dioxus_config.web.app.base_path {
Some(path) => path, Some(path) => path,
None => ".", None => ".",
}; };
let app_name = &config.application.name; let app_name = &config.dioxus_config.application.name;
// Check if a script already exists // Check if a script already exists
if html.contains("{app_name}") && html.contains("{base_path}") { if html.contains("{app_name}") && html.contains("{base_path}") {
html = html.replace("{app_name}", app_name); html = html.replace("{app_name}", app_name);
@@ -547,6 +578,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
} }
let title = config let title = config
.dioxus_config
.web .web
.app .app
.title .title
@@ -716,3 +748,42 @@ fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
Ok(result) Ok(result)
} }
/// Process any assets collected from the binary
fn process_assets(config: &CrateConfig) -> anyhow::Result<()> {
let manifest = config.asset_manifest();
let static_asset_output_dir = PathBuf::from(
config
.dioxus_config
.web
.app
.base_path
.clone()
.unwrap_or_default(),
);
let static_asset_output_dir = config.out_dir.join(static_asset_output_dir);
manifest.copy_static_assets_to(static_asset_output_dir)?;
Ok(())
}
pub(crate) struct WebAssetConfigDropGuard;
impl WebAssetConfigDropGuard {
pub fn new() -> Self {
// Set up the collect asset config
manganis_cli_support::Config::default()
.with_assets_serve_location("/")
.save();
Self {}
}
}
impl Drop for WebAssetConfigDropGuard {
fn drop(&mut self) {
// Reset the config
manganis_cli_support::Config::default().save();
}
}
+46 -10
View File
@@ -1,6 +1,8 @@
use crate::cfg::Platform;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use crate::plugin::PluginManager; use crate::plugin::PluginManager;
use crate::server::fullstack::FullstackServerEnvGuard;
use crate::server::fullstack::FullstackWebEnvGuard;
use crate::{cfg::Platform, WebAssetConfigDropGuard};
use super::*; use super::*;
@@ -13,23 +15,26 @@ pub struct Build {
} }
impl Build { impl Build {
pub fn build(self, bin: Option<PathBuf>) -> Result<()> { pub fn build(self, bin: Option<PathBuf>, target_dir: Option<&std::path::Path>) -> Result<()> {
let mut crate_config = crate::CrateConfig::new(bin)?; let mut crate_config = crate::CrateConfig::new(bin)?;
if let Some(target_dir) = target_dir {
crate_config.target_dir = target_dir.to_path_buf();
}
// change the release state. // change the release state.
crate_config.with_release(self.build.release); crate_config.with_release(self.build.release);
crate_config.with_verbose(self.build.verbose); crate_config.with_verbose(self.build.verbose);
if self.build.example.is_some() { if self.build.example.is_some() {
crate_config.as_example(self.build.example.unwrap()); crate_config.as_example(self.build.example.clone().unwrap());
} }
if self.build.profile.is_some() { if self.build.profile.is_some() {
crate_config.set_profile(self.build.profile.unwrap()); crate_config.set_profile(self.build.profile.clone().unwrap());
} }
if self.build.features.is_some() { if self.build.features.is_some() {
crate_config.set_features(self.build.features.unwrap()); crate_config.set_features(self.build.features.clone().unwrap());
} }
let platform = self let platform = self
@@ -37,25 +42,56 @@ impl Build {
.platform .platform
.unwrap_or(crate_config.dioxus_config.application.default_platform); .unwrap_or(crate_config.dioxus_config.application.default_platform);
if let Some(target) = self.build.target { if let Some(target) = self.build.target.clone() {
crate_config.set_target(target); crate_config.set_target(target);
} }
crate_config.set_cargo_args(self.build.cargo_args); crate_config.set_cargo_args(self.build.cargo_args.clone());
// #[cfg(feature = "plugin")] // #[cfg(feature = "plugin")]
// let _ = PluginManager::on_build_start(&crate_config, &platform); // let _ = PluginManager::on_build_start(&crate_config, &platform);
match platform { match platform {
Platform::Web => { Platform::Web => {
crate::builder::build(&crate_config, true)?; crate::builder::build(&crate_config, false, self.build.skip_assets)?;
} }
Platform::Desktop => { Platform::Desktop => {
crate::builder::build_desktop(&crate_config, false)?; crate::builder::build_desktop(&crate_config, false, self.build.skip_assets)?;
}
Platform::Fullstack => {
// Fullstack mode must be built with web configs on the desktop (server) binary as well as the web binary
let _config = WebAssetConfigDropGuard::new();
{
let mut web_config = crate_config.clone();
let _gaurd = FullstackWebEnvGuard::new(&self.build);
let web_feature = self.build.client_feature;
let features = &mut web_config.features;
match features {
Some(features) => {
features.push(web_feature);
}
None => web_config.features = Some(vec![web_feature]),
};
crate::builder::build(&crate_config, false, self.build.skip_assets)?;
}
{
let mut desktop_config = crate_config.clone();
let desktop_feature = self.build.server_feature;
let features = &mut desktop_config.features;
match features {
Some(features) => {
features.push(desktop_feature);
}
None => desktop_config.features = Some(vec![desktop_feature]),
};
let _gaurd =
FullstackServerEnvGuard::new(self.build.force_debug, self.build.release);
crate::builder::build_desktop(&desktop_config, false, self.build.skip_assets)?;
}
} }
} }
let temp = gen_page(&crate_config.dioxus_config, false); let temp = gen_page(&crate_config, false, self.build.skip_assets);
let mut file = std::fs::File::create( let mut file = std::fs::File::create(
crate_config crate_config
+14 -1
View File
@@ -83,7 +83,7 @@ impl Bundle {
crate_config.set_cargo_args(self.build.cargo_args); crate_config.set_cargo_args(self.build.cargo_args);
// build the desktop app // build the desktop app
build_desktop(&crate_config, false)?; build_desktop(&crate_config, false, false)?;
// copy the binary to the out dir // copy the binary to the out dir
let package = crate_config.manifest.package.unwrap(); let package = crate_config.manifest.package.unwrap();
@@ -134,6 +134,19 @@ impl Bundle {
} }
} }
// Add all assets from collect assets to the bundle
{
let config = manganis_cli_support::Config::current();
let location = config.assets_serve_location().to_string();
let location = format!("./{}", location);
println!("Adding assets from {} to bundle", location);
if let Some(resources) = &mut bundle_settings.resources {
resources.push(location);
} else {
bundle_settings.resources = Some(vec![location]);
}
}
let mut settings = SettingsBuilder::new() let mut settings = SettingsBuilder::new()
.project_out_directory(crate_config.out_dir) .project_out_directory(crate_config.out_dir)
.package_settings(PackageSettings { .package_settings(PackageSettings {
+59 -1
View File
@@ -11,6 +11,11 @@ pub struct ConfigOptsBuild {
#[serde(default)] #[serde(default)]
pub release: bool, pub release: bool,
/// This flag only applies to fullstack builds. By default fullstack builds will run with something in between debug and release mode. This flag will force the build to run in debug mode. [default: false]
#[clap(long)]
#[serde(default)]
pub force_debug: bool,
// Use verbose output [default: false] // Use verbose output [default: false]
#[clap(long)] #[clap(long)]
#[serde(default)] #[serde(default)]
@@ -28,10 +33,23 @@ pub struct ConfigOptsBuild {
#[clap(long, value_enum)] #[clap(long, value_enum)]
pub platform: Option<Platform>, pub platform: Option<Platform>,
/// Skip collecting assets from dependencies [default: false]
#[clap(long)]
#[serde(default)]
pub skip_assets: bool,
/// Space separated list of features to activate /// Space separated list of features to activate
#[clap(long)] #[clap(long)]
pub features: Option<Vec<String>>, pub features: Option<Vec<String>>,
/// The feature to use for the client in a fullstack app [default: "web"]
#[clap(long, default_value_t = { "web".to_string() })]
pub client_feature: String,
/// The feature to use for the server in a fullstack app [default: "ssr"]
#[clap(long, default_value_t = { "ssr".to_string() })]
pub server_feature: String,
/// Rustc platform triple /// Rustc platform triple
#[clap(long)] #[clap(long)]
pub target: Option<String>, pub target: Option<String>,
@@ -41,6 +59,25 @@ pub struct ConfigOptsBuild {
pub cargo_args: Vec<String>, pub cargo_args: Vec<String>,
} }
impl From<ConfigOptsServe> for ConfigOptsBuild {
fn from(serve: ConfigOptsServe) -> Self {
Self {
target: serve.target,
release: serve.release,
verbose: serve.verbose,
example: serve.example,
profile: serve.profile,
platform: serve.platform,
features: serve.features,
client_feature: serve.client_feature,
server_feature: serve.server_feature,
skip_assets: serve.skip_assets,
force_debug: serve.force_debug,
cargo_args: serve.cargo_args,
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Parser)] #[derive(Clone, Debug, Default, Deserialize, Parser)]
pub struct ConfigOptsServe { pub struct ConfigOptsServe {
/// Port of dev server /// Port of dev server
@@ -62,6 +99,11 @@ pub struct ConfigOptsServe {
#[serde(default)] #[serde(default)]
pub release: bool, pub release: bool,
/// This flag only applies to fullstack builds. By default fullstack builds will run with something in between debug and release mode. This flag will force the build to run in debug mode. [default: false]
#[clap(long)]
#[serde(default)]
pub force_debug: bool,
// Use verbose output [default: false] // Use verbose output [default: false]
#[clap(long)] #[clap(long)]
#[serde(default)] #[serde(default)]
@@ -71,7 +113,7 @@ pub struct ConfigOptsServe {
#[clap(long)] #[clap(long)]
pub profile: Option<String>, pub profile: Option<String>,
/// Build platform: support Web & Desktop [default: "default_platform"] /// Build platform: support Web, Desktop, and Fullstack [default: "default_platform"]
#[clap(long, value_enum)] #[clap(long, value_enum)]
pub platform: Option<Platform>, pub platform: Option<Platform>,
@@ -90,6 +132,19 @@ pub struct ConfigOptsServe {
#[clap(long)] #[clap(long)]
pub features: Option<Vec<String>>, pub features: Option<Vec<String>>,
/// Skip collecting assets from dependencies [default: false]
#[clap(long)]
#[serde(default)]
pub skip_assets: bool,
/// The feature to use for the client in a fullstack app [default: "web"]
#[clap(long, default_value_t = { "web".to_string() })]
pub client_feature: String,
/// The feature to use for the server in a fullstack app [default: "ssr"]
#[clap(long, default_value_t = { "ssr".to_string() })]
pub server_feature: String,
/// Rustc platform triple /// Rustc platform triple
#[clap(long)] #[clap(long)]
pub target: Option<String>, pub target: Option<String>,
@@ -107,6 +162,9 @@ pub enum Platform {
#[clap(name = "desktop")] #[clap(name = "desktop")]
#[serde(rename = "desktop")] #[serde(rename = "desktop")]
Desktop, Desktop,
#[clap(name = "fullstack")]
#[serde(rename = "fullstack")]
Fullstack,
} }
/// Config options for the bundling system. /// Config options for the bundling system.
+6
View File
@@ -28,6 +28,12 @@ impl Clean {
remove_dir_all(crate_config.crate_dir.join(&out_dir))?; remove_dir_all(crate_config.crate_dir.join(&out_dir))?;
} }
let fullstack_out_dir = crate_config.crate_dir.join(".dioxus");
if fullstack_out_dir.is_dir() {
remove_dir_all(fullstack_out_dir)?;
}
Ok(()) Ok(())
} }
} }
+15 -6
View File
@@ -12,6 +12,7 @@ pub struct Serve {
impl Serve { impl Serve {
pub async fn serve(self, bin: Option<PathBuf>) -> Result<()> { pub async fn serve(self, bin: Option<PathBuf>) -> Result<()> {
let mut crate_config = crate::CrateConfig::new(bin)?; let mut crate_config = crate::CrateConfig::new(bin)?;
let serve_cfg = self.serve.clone();
// change the relase state. // change the relase state.
crate_config.with_hot_reload(self.serve.hot_reload); crate_config.with_hot_reload(self.serve.hot_reload);
@@ -48,21 +49,29 @@ impl Serve {
match platform { match platform {
cfg::Platform::Web => { cfg::Platform::Web => {
// generate dev-index page // generate dev-index page
Serve::regen_dev_page(&crate_config)?; Serve::regen_dev_page(&crate_config, self.serve.skip_assets)?;
// start the develop server // start the develop server
server::web::startup(self.serve.port, crate_config.clone(), self.serve.open) server::web::startup(
.await?; self.serve.port,
crate_config.clone(),
self.serve.open,
self.serve.skip_assets,
)
.await?;
} }
cfg::Platform::Desktop => { cfg::Platform::Desktop => {
server::desktop::startup(crate_config.clone()).await?; server::desktop::startup(crate_config.clone(), &serve_cfg).await?;
}
cfg::Platform::Fullstack => {
server::fullstack::startup(crate_config.clone(), &serve_cfg).await?;
} }
} }
Ok(()) Ok(())
} }
pub fn regen_dev_page(crate_config: &CrateConfig) -> Result<()> { pub fn regen_dev_page(crate_config: &CrateConfig, skip_assets: bool) -> Result<()> {
let serve_html = gen_page(&crate_config.dioxus_config, true); let serve_html = gen_page(crate_config, true, skip_assets);
let dist_path = crate_config.crate_dir.join( let dist_path = crate_config.crate_dir.join(
crate_config crate_config
+9
View File
@@ -1,4 +1,6 @@
use crate::{cfg::Platform, error::Result}; use crate::{cfg::Platform, error::Result};
use manganis_cli_support::AssetManifest;
use manganis_cli_support::AssetManifestExt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap, collections::HashMap,
@@ -303,6 +305,13 @@ impl CrateConfig {
}) })
} }
pub fn asset_manifest(&self) -> AssetManifest {
AssetManifest::load_from_path(
self.crate_dir.join("Cargo.toml"),
self.workspace_dir.join("Cargo.lock"),
)
}
pub fn as_example(&mut self, example_name: String) -> &mut Self { pub fn as_example(&mut self, example_name: String) -> &mut Self {
self.executable = ExecutableType::Example(example_name); self.executable = ExecutableType::Example(example_name);
self self
+1 -1
View File
@@ -63,7 +63,7 @@ async fn main() -> anyhow::Result<()> {
.map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)), .map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)),
Build(opts) if bin.is_ok() => opts Build(opts) if bin.is_ok() => opts
.build(Some(bin.unwrap().clone())) .build(Some(bin.unwrap().clone()), None)
.map_err(|e| anyhow!("🚫 Building project failed: {}", e)), .map_err(|e| anyhow!("🚫 Building project failed: {}", e)),
Clean(opts) if bin.is_ok() => opts Clean(opts) if bin.is_ok() => opts
+72 -32
View File
@@ -1,11 +1,12 @@
use crate::server::Platform;
use crate::{ use crate::{
cfg::ConfigOptsServe,
server::{ server::{
output::{print_console_info, PrettierOptions}, output::{print_console_info, PrettierOptions},
setup_file_watcher, setup_file_watcher,
}, },
BuildResult, CrateConfig, Result, BuildResult, CrateConfig, Result,
}; };
use dioxus_hot_reload::HotReloadMsg; use dioxus_hot_reload::HotReloadMsg;
use dioxus_html::HtmlCtx; use dioxus_html::HtmlCtx;
use dioxus_rsx::hot_reload::*; use dioxus_rsx::hot_reload::*;
@@ -21,7 +22,14 @@ use plugin::PluginManager;
use super::HotReloadState; use super::HotReloadState;
pub async fn startup(config: CrateConfig) -> Result<()> { pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
startup_with_platform::<DesktopPlatform>(config, serve).await
}
pub(crate) async fn startup_with_platform<P: Platform + Send + 'static>(
config: CrateConfig,
serve_cfg: &ConfigOptsServe,
) -> Result<()> {
// ctrl-c shutdown checker // ctrl-c shutdown checker
let _crate_config = config.clone(); let _crate_config = config.clone();
let _ = ctrlc::set_handler(move || { let _ = ctrlc::set_handler(move || {
@@ -51,15 +59,18 @@ pub async fn startup(config: CrateConfig) -> Result<()> {
false => None, false => None,
}; };
serve(config, hot_reload_state).await?; serve::<P>(config, serve_cfg, hot_reload_state).await?;
Ok(()) Ok(())
} }
/// Start the server without hot reload /// Start the server without hot reload
pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>) -> Result<()> { async fn serve<P: Platform + Send + 'static>(
let (child, first_build_result) = start_desktop(&config)?; config: CrateConfig,
let currently_running_child: RwLock<Child> = RwLock::new(child); serve: &ConfigOptsServe,
hot_reload_state: Option<HotReloadState>,
) -> Result<()> {
let platform = RwLock::new(P::start(&config, serve)?);
log::info!("🚀 Starting development server..."); log::info!("🚀 Starting development server...");
@@ -68,15 +79,7 @@ pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>
let _watcher = setup_file_watcher( let _watcher = setup_file_watcher(
{ {
let config = config.clone(); let config = config.clone();
move || platform.write().unwrap().rebuild(&config)
move || {
let mut current_child = currently_running_child.write().unwrap();
log::trace!("Killing old process");
current_child.kill()?;
let (child, result) = start_desktop(&config)?;
*current_child = child;
Ok(result)
}
}, },
&config, &config,
None, None,
@@ -84,19 +87,9 @@ pub async fn serve(config: CrateConfig, hot_reload_state: Option<HotReloadState>
) )
.await?; .await?;
// Print serve info
print_console_info(
&config,
PrettierOptions {
changed: vec![],
warnings: first_build_result.warnings,
elapsed_time: first_build_result.elapsed_time,
},
None,
);
match hot_reload_state { match hot_reload_state {
Some(hot_reload_state) => { Some(hot_reload_state) => {
// The open interprocess sockets
start_desktop_hot_reload(hot_reload_state).await?; start_desktop_hot_reload(hot_reload_state).await?;
} }
None => { None => {
@@ -192,7 +185,7 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<()
} }
fn clear_paths(file_socket_path: &std::path::Path) { fn clear_paths(file_socket_path: &std::path::Path) {
if cfg!(target_os = "macos") { if cfg!(unix) {
// On unix, if you force quit the application, it can leave the file socket open // On unix, if you force quit the application, it can leave the file socket open
// This will cause the local socket listener to fail to open // This will cause the local socket listener to fail to open
// We check if the file socket is already open from an old session and then delete it // We check if the file socket is already open from an old session and then delete it
@@ -217,10 +210,9 @@ fn send_msg(msg: HotReloadMsg, channel: &mut impl std::io::Write) -> bool {
} }
} }
pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> { fn start_desktop(config: &CrateConfig, skip_assets: bool) -> Result<(RAIIChild, BuildResult)> {
// Run the desktop application // Run the desktop application
log::trace!("Building application"); let result = crate::builder::build_desktop(config, true, skip_assets)?;
let result = crate::builder::build_desktop(config, true)?;
match &config.executable { match &config.executable {
crate::ExecutableType::Binary(name) crate::ExecutableType::Binary(name)
@@ -230,10 +222,58 @@ pub fn start_desktop(config: &CrateConfig) -> Result<(Child, BuildResult)> {
if cfg!(windows) { if cfg!(windows) {
file.set_extension("exe"); file.set_extension("exe");
} }
log::trace!("Running application from {:?}", file); let active = "DIOXUS_ACTIVE";
let child = Command::new(file.to_str().unwrap()).spawn()?; let child = RAIIChild(
Command::new(file.to_str().unwrap())
.env(active, "true")
.spawn()?,
);
Ok((child, result)) Ok((child, result))
} }
} }
} }
pub(crate) struct DesktopPlatform {
currently_running_child: RAIIChild,
skip_assets: bool,
}
impl Platform for DesktopPlatform {
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self> {
let (child, first_build_result) = start_desktop(config, serve.skip_assets)?;
log::info!("🚀 Starting development server...");
// Print serve info
print_console_info(
config,
PrettierOptions {
changed: vec![],
warnings: first_build_result.warnings,
elapsed_time: first_build_result.elapsed_time,
},
None,
);
Ok(Self {
currently_running_child: child,
skip_assets: serve.skip_assets,
})
}
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult> {
self.currently_running_child.0.kill()?;
let (child, result) = start_desktop(config, self.skip_assets)?;
self.currently_running_child = child;
Ok(result)
}
}
struct RAIIChild(Child);
impl Drop for RAIIChild {
fn drop(&mut self) {
let _ = self.0.kill();
}
}
+161
View File
@@ -0,0 +1,161 @@
use crate::{
cfg::{ConfigOptsBuild, ConfigOptsServe},
CrateConfig, Result, WebAssetConfigDropGuard,
};
use super::{desktop, Platform};
pub async fn startup(config: CrateConfig, serve: &ConfigOptsServe) -> Result<()> {
desktop::startup_with_platform::<FullstackPlatform>(config, serve).await
}
fn start_web_build_thread(
config: &CrateConfig,
serve: &ConfigOptsServe,
) -> std::thread::JoinHandle<Result<()>> {
let serve = serve.clone();
let target_directory = config.crate_dir.join(".dioxus").join("web");
std::fs::create_dir_all(&target_directory).unwrap();
std::thread::spawn(move || build_web(serve, &target_directory))
}
struct FullstackPlatform {
serve: ConfigOptsServe,
desktop: desktop::DesktopPlatform,
_config: WebAssetConfigDropGuard,
}
impl Platform for FullstackPlatform {
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
where
Self: Sized,
{
let thread_handle = start_web_build_thread(config, serve);
let mut desktop_config = config.clone();
let desktop_feature = serve.server_feature.clone();
let features = &mut desktop_config.features;
match features {
Some(features) => {
features.push(desktop_feature);
}
None => desktop_config.features = Some(vec![desktop_feature]),
};
let config = WebAssetConfigDropGuard::new();
let desktop = desktop::DesktopPlatform::start(&desktop_config, serve)?;
thread_handle
.join()
.map_err(|_| anyhow::anyhow!("Failed to join thread"))??;
Ok(Self {
desktop,
serve: serve.clone(),
_config: config,
})
}
fn rebuild(&mut self, crate_config: &CrateConfig) -> Result<crate::BuildResult> {
let thread_handle = start_web_build_thread(crate_config, &self.serve);
let result = {
let mut desktop_config = crate_config.clone();
let desktop_feature = self.serve.server_feature.clone();
let features = &mut desktop_config.features;
match features {
Some(features) => {
features.push(desktop_feature);
}
None => desktop_config.features = Some(vec![desktop_feature]),
};
let _gaurd = FullstackServerEnvGuard::new(self.serve.force_debug, self.serve.release);
self.desktop.rebuild(&desktop_config)
};
thread_handle
.join()
.map_err(|_| anyhow::anyhow!("Failed to join thread"))??;
result
}
}
fn build_web(serve: ConfigOptsServe, target_directory: &std::path::Path) -> Result<()> {
let mut web_config: ConfigOptsBuild = serve.into();
let web_feature = web_config.client_feature.clone();
let features = &mut web_config.features;
match features {
Some(features) => {
features.push(web_feature);
}
None => web_config.features = Some(vec![web_feature]),
};
web_config.platform = Some(crate::cfg::Platform::Web);
let _gaurd = FullstackWebEnvGuard::new(&web_config);
crate::cli::build::Build { build: web_config }.build(None, Some(target_directory))
}
// Debug mode web builds have a very large size by default. If debug mode is not enabled, we strip some of the debug info by default
// This reduces a hello world from ~40MB to ~2MB
pub(crate) struct FullstackWebEnvGuard {
old_rustflags: Option<String>,
}
impl FullstackWebEnvGuard {
pub fn new(serve: &ConfigOptsBuild) -> Self {
Self {
old_rustflags: (!serve.force_debug).then(|| {
let old_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
let debug_assertions = if serve.release {
""
} else {
" -C debug-assertions"
};
std::env::set_var(
"RUSTFLAGS",
format!(
"{old_rustflags} -C debuginfo=none -C strip=debuginfo{debug_assertions}"
),
);
old_rustflags
}),
}
}
}
impl Drop for FullstackWebEnvGuard {
fn drop(&mut self) {
if let Some(old_rustflags) = self.old_rustflags.take() {
std::env::set_var("RUSTFLAGS", old_rustflags);
}
}
}
// Debug mode web builds have a very large size by default. If debug mode is not enabled, we strip some of the debug info by default
// This reduces a hello world from ~40MB to ~2MB
pub(crate) struct FullstackServerEnvGuard {
old_rustflags: Option<String>,
}
impl FullstackServerEnvGuard {
pub fn new(debug: bool, release: bool) -> Self {
Self {
old_rustflags: (!debug).then(|| {
let old_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
let debug_assertions = if release { "" } else { " -C debug-assertions" };
std::env::set_var(
"RUSTFLAGS",
format!("{old_rustflags} -C opt-level=2 {debug_assertions}"),
);
old_rustflags
}),
}
}
}
impl Drop for FullstackServerEnvGuard {
fn drop(&mut self) {
if let Some(old_rustflags) = self.old_rustflags.take() {
std::env::set_var("RUSTFLAGS", old_rustflags);
}
}
}
+9 -1
View File
@@ -1,4 +1,4 @@
use crate::{BuildResult, CrateConfig, Result}; use crate::{cfg::ConfigOptsServe, BuildResult, CrateConfig, Result};
use cargo_metadata::diagnostic::Diagnostic; use cargo_metadata::diagnostic::Diagnostic;
use dioxus_core::Template; use dioxus_core::Template;
@@ -14,6 +14,7 @@ use tokio::sync::broadcast::{self};
mod output; mod output;
use output::*; use output::*;
pub mod desktop; pub mod desktop;
pub mod fullstack;
pub mod web; pub mod web;
/// Sets up a file watcher /// Sets up a file watcher
@@ -141,6 +142,13 @@ async fn setup_file_watcher<F: Fn() -> Result<BuildResult> + Send + 'static>(
Ok(watcher) Ok(watcher)
} }
pub(crate) trait Platform {
fn start(config: &CrateConfig, serve: &ConfigOptsServe) -> Result<Self>
where
Self: Sized;
fn rebuild(&mut self, config: &CrateConfig) -> Result<BuildResult>;
}
#[derive(Clone)] #[derive(Clone)]
pub struct HotReloadState { pub struct HotReloadState {
pub messages: broadcast::Sender<Template<'static>>, pub messages: broadcast::Sender<Template<'static>>,
+21 -7
View File
@@ -48,7 +48,12 @@ struct WsReloadState {
update: broadcast::Sender<()>, update: broadcast::Sender<()>,
} }
pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Result<()> { pub async fn startup(
port: u16,
config: CrateConfig,
start_browser: bool,
skip_assets: bool,
) -> Result<()> {
// ctrl-c shutdown checker // ctrl-c shutdown checker
let _crate_config = config.clone(); let _crate_config = config.clone();
let _ = ctrlc::set_handler(move || { let _ = ctrlc::set_handler(move || {
@@ -80,7 +85,15 @@ pub async fn startup(port: u16, config: CrateConfig, start_browser: bool) -> Res
false => None, false => None,
}; };
serve(ip, port, config, start_browser, hot_reload_state).await?; serve(
ip,
port,
config,
start_browser,
skip_assets,
hot_reload_state,
)
.await?;
Ok(()) Ok(())
} }
@@ -91,9 +104,10 @@ pub async fn serve(
port: u16, port: u16,
config: CrateConfig, config: CrateConfig,
start_browser: bool, start_browser: bool,
skip_assets: bool,
hot_reload_state: Option<HotReloadState>, hot_reload_state: Option<HotReloadState>,
) -> Result<()> { ) -> Result<()> {
let first_build_result = crate::builder::build(&config, true)?; let first_build_result = crate::builder::build(&config, false, skip_assets)?;
log::info!("🚀 Starting development server..."); log::info!("🚀 Starting development server...");
@@ -106,7 +120,7 @@ pub async fn serve(
{ {
let config = config.clone(); let config = config.clone();
let reload_tx = reload_tx.clone(); let reload_tx = reload_tx.clone();
move || build(&config, &reload_tx) move || build(&config, &reload_tx, skip_assets)
}, },
&config, &config,
Some(WebServerInfo { Some(WebServerInfo {
@@ -420,8 +434,8 @@ async fn ws_handler(
}) })
} }
fn build(config: &CrateConfig, reload_tx: &Sender<()>) -> Result<BuildResult> { fn build(config: &CrateConfig, reload_tx: &Sender<()>, skip_assets: bool) -> Result<BuildResult> {
let result = builder::build(config, true)?; let result = builder::build(config, true, skip_assets)?;
// change the websocket reload state to true; // change the websocket reload state to true;
// the page will auto-reload. // the page will auto-reload.
if config if config
@@ -431,7 +445,7 @@ fn build(config: &CrateConfig, reload_tx: &Sender<()>) -> Result<BuildResult> {
.reload_html .reload_html
.unwrap_or(false) .unwrap_or(false)
{ {
let _ = Serve::regen_dev_page(config); let _ = Serve::regen_dev_page(config, skip_assets);
} }
let _ = reload_tx.send(()); let _ = reload_tx.send(());
Ok(result) Ok(result)
+1 -1
View File
@@ -98,7 +98,7 @@ impl Runtime {
} }
} }
/// A gaurd for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime. /// A guard for a new runtime. This must be used to override the current runtime when importing components from a dynamic library that has it's own runtime.
/// ///
/// ```rust /// ```rust
/// use dioxus::prelude::*; /// use dioxus::prelude::*;
+25 -25
View File
@@ -1,13 +1,33 @@
//! Verify that tasks get polled by the virtualdom properly, and that we escape wait_for_work safely //! Verify that tasks get polled by the virtualdom properly, and that we escape wait_for_work safely
use dioxus::prelude::*;
use std::{sync::atomic::AtomicUsize, time::Duration};
static POLL_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cfg(not(miri))] #[cfg(not(miri))]
#[tokio::test] #[tokio::test]
async fn it_works() { async fn it_works() {
use dioxus::prelude::*;
use std::{sync::atomic::AtomicUsize, time::Duration};
static POLL_COUNT: AtomicUsize = AtomicUsize::new(0);
fn app(cx: Scope) -> Element {
cx.use_hook(|| {
cx.spawn(async {
for x in 0..10 {
tokio::time::sleep(Duration::from_micros(50)).await;
POLL_COUNT.fetch_add(x, std::sync::atomic::Ordering::Relaxed);
}
});
cx.spawn(async {
for x in 0..10 {
tokio::time::sleep(Duration::from_micros(25)).await;
POLL_COUNT.fetch_add(x * 2, std::sync::atomic::Ordering::Relaxed);
}
});
});
cx.render(rsx!(()))
}
let mut dom = VirtualDom::new(app); let mut dom = VirtualDom::new(app);
let _ = dom.rebuild(); let _ = dom.rebuild();
@@ -24,23 +44,3 @@ async fn it_works() {
135 135
); );
} }
fn app(cx: Scope) -> Element {
cx.use_hook(|| {
cx.spawn(async {
for x in 0..10 {
tokio::time::sleep(Duration::from_micros(50)).await;
POLL_COUNT.fetch_add(x, std::sync::atomic::Ordering::Relaxed);
}
});
cx.spawn(async {
for x in 0..10 {
tokio::time::sleep(Duration::from_micros(25)).await;
POLL_COUNT.fetch_add(x * 2, std::sync::atomic::Ordering::Relaxed);
}
});
});
cx.render(rsx!(()))
}
+3 -2
View File
@@ -18,7 +18,7 @@ dioxus-hot-reload = { workspace = true, optional = true }
serde = "1.0.136" serde = "1.0.136"
serde_json = "1.0.79" serde_json = "1.0.79"
thiserror = { workspace = true } thiserror = { workspace = true }
tracing = { workspace = true } tracing.workspace = true
wry = { version = "0.34.0", default-features = false, features = ["tao", "protocol", "file-drop"] } wry = { version = "0.34.0", default-features = false, features = ["tao", "protocol", "file-drop"] }
futures-channel = { workspace = true } futures-channel = { workspace = true }
tokio = { workspace = true, features = [ tokio = { workspace = true, features = [
@@ -40,8 +40,9 @@ urlencoding = "2.1.2"
async-trait = "0.1.68" async-trait = "0.1.68"
crossbeam-channel = "0.5.8" crossbeam-channel = "0.5.8"
[target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies] [target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies]
# This is only for debug mode, and it appears mobile does not support some packages this uses
manganis-cli-support = { git = "https://github.com/DioxusLabs/collect-assets", features = ["webp", "html"] }
rfd = "0.12" rfd = "0.12"
global-hotkey = { git = "https://github.com/tauri-apps/global-hotkey" } global-hotkey = { git = "https://github.com/tauri-apps/global-hotkey" }
+1 -1
View File
@@ -17,7 +17,7 @@ pub(crate) fn check_app_exits(app: Component) {
dioxus_desktop::launch_cfg( dioxus_desktop::launch_cfg(
app, app,
Config::new().with_window(WindowBuilder::new().with_visible(false)), Config::new().with_window(WindowBuilder::new().with_visible(true)),
); );
// Stop deadman's switch // Stop deadman's switch
+1 -1
View File
@@ -16,7 +16,7 @@ pub(crate) fn check_app_exits(app: Component) {
dioxus_desktop::launch_cfg( dioxus_desktop::launch_cfg(
app, app,
Config::new().with_window(WindowBuilder::new().with_visible(false)), Config::new().with_window(WindowBuilder::new().with_visible(true)),
); );
should_panic.store(false, std::sync::atomic::Ordering::SeqCst); should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
+60
View File
@@ -0,0 +1,60 @@
pub fn copy_assets() {
#[cfg(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
))]
{
// The CLI will copy assets to the current working directory
if std::env::var_os("DIOXUS_ACTIVE").is_some() {
return;
}
use manganis_cli_support::AssetManifest;
use manganis_cli_support::AssetManifestExt;
use manganis_cli_support::Config;
use std::path::PathBuf;
let config = Config::current();
let asset_location = config.assets_serve_location();
let asset_location = PathBuf::from(asset_location);
let _ = std::fs::remove_dir_all(&asset_location);
println!("Finding assets... (Note: if you run a dioxus desktop application with the CLI. This process will be significantly faster.)");
let manifest = AssetManifest::load();
let has_assets = manifest
.packages()
.iter()
.any(|package| !package.assets().is_empty());
if has_assets {
println!("Copying and optimizing assets...");
manifest.copy_static_assets_to(&asset_location).unwrap();
println!("Copied assets to {}", asset_location.display());
} else {
println!("No assets found");
}
}
#[cfg(not(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
)))]
{
println!(
"Skipping assets in release mode. You compile assets with the dioxus-cli in release mode"
);
}
}
+4
View File
@@ -4,6 +4,7 @@
#![deny(missing_docs)] #![deny(missing_docs)]
mod cfg; mod cfg;
mod collect_assets;
mod desktop_context; mod desktop_context;
mod element; mod element;
mod escape; mod escape;
@@ -146,6 +147,9 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config)
} }
}); });
// Copy over any assets we find
crate::collect_assets::copy_assets();
// Set the event converter // Set the event converter
dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter)); dioxus_html::set_event_converter(Box::new(SerializedHtmlEventConverter));
+55 -8
View File
@@ -216,6 +216,7 @@ pub(super) async fn desktop_handler(
request: Request<Vec<u8>>, request: Request<Vec<u8>>,
custom_head: Option<String>, custom_head: Option<String>,
custom_index: Option<String>, custom_index: Option<String>,
#[allow(unused_variables)] assets_head: Option<String>,
root_name: &str, root_name: &str,
asset_handlers: &AssetHandlerRegistry, asset_handlers: &AssetHandlerRegistry,
edit_queue: &EditQueue, edit_queue: &EditQueue,
@@ -240,9 +241,46 @@ pub(super) async fn desktop_handler(
// Otherwise, we'll serve the default index.html and apply a custom head if that's specified. // Otherwise, we'll serve the default index.html and apply a custom head if that's specified.
let mut template = include_str!("./index.html").to_string(); let mut template = include_str!("./index.html").to_string();
if let Some(custom_head) = custom_head { #[allow(unused_mut)]
template = template.replace("<!-- CUSTOM HEAD -->", &custom_head); let mut head = custom_head.unwrap_or_default();
#[cfg(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
))]
{
use manganis_cli_support::AssetManifestExt;
let manifest = manganis_cli_support::AssetManifest::load();
head += &manifest.head();
} }
#[cfg(not(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
)))]
{
if let Some(assets_head) = assets_head {
head += &assets_head;
} else {
tracing::warn!("No assets head found. You can compile assets with the dioxus-cli in release mode");
}
}
template = template.replace("<!-- CUSTOM HEAD -->", &head);
template template
.replace( .replace(
@@ -279,12 +317,10 @@ pub(super) async fn desktop_handler(
// Else, try to serve a file from the filesystem. // Else, try to serve a file from the filesystem.
// If the path is relative, we'll try to serve it from the assets directory. // If the path is relative, we'll try to serve it from the assets directory.
let mut asset = get_asset_root() let mut asset = get_asset_root_or_default().join(&request.path);
.unwrap_or_else(|| Path::new(".").to_path_buf())
.join(&request.path);
if !asset.exists() { if !asset.exists() {
asset = PathBuf::from("/").join(request.path); asset = PathBuf::from("/").join(&request.path);
} }
if asset.exists() { if asset.exists() {
@@ -295,7 +331,7 @@ pub(super) async fn desktop_handler(
return; return;
} }
}; };
let asset = match std::fs::read(asset) { let asset = match std::fs::read(&asset) {
Ok(asset) => asset, Ok(asset) => asset,
Err(err) => { Err(err) => {
tracing::error!("error reading asset: {}", err); tracing::error!("error reading asset: {}", err);
@@ -314,6 +350,12 @@ pub(super) async fn desktop_handler(
} }
} }
tracing::error!(
"Failed to find {} (as path {})",
request.uri().path(),
asset.display()
);
match Response::builder() match Response::builder()
.status(StatusCode::NOT_FOUND) .status(StatusCode::NOT_FOUND)
.body(Cow::from(String::from("Not Found").into_bytes())) .body(Cow::from(String::from("Not Found").into_bytes()))
@@ -325,6 +367,11 @@ pub(super) async fn desktop_handler(
} }
} }
#[allow(unreachable_code)]
pub(crate) fn get_asset_root_or_default() -> PathBuf {
get_asset_root().unwrap_or_else(|| Path::new(".").to_path_buf())
}
#[allow(unreachable_code)] #[allow(unreachable_code)]
fn get_asset_root() -> Option<PathBuf> { fn get_asset_root() -> Option<PathBuf> {
/* /*
@@ -339,7 +386,7 @@ fn get_asset_root() -> Option<PathBuf> {
*/ */
if std::env::var_os("CARGO").is_some() { if std::env::var_os("CARGO").is_some() || std::env::var_os("DIOXUS_ACTIVE").is_some() {
return None; return None;
} }
+42
View File
@@ -18,6 +18,46 @@ pub(crate) fn build(
let custom_head = cfg.custom_head.clone(); let custom_head = cfg.custom_head.clone();
let index_file = cfg.custom_index.clone(); let index_file = cfg.custom_index.clone();
let root_name = cfg.root_name.clone(); let root_name = cfg.root_name.clone();
let assets_head = {
#[cfg(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
))]
{
None
}
#[cfg(not(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
)))]
{
let head = crate::protocol::get_asset_root_or_default();
let head = head.join("dist/__assets_head.html");
match std::fs::read_to_string(&head) {
Ok(s) => Some(s),
Err(err) => {
tracing::error!("Failed to read {head:?}: {err}");
None
}
}
}
};
// TODO: restore the menu bar with muda: https://github.com/tauri-apps/muda/blob/dev/examples/wry.rs // TODO: restore the menu bar with muda: https://github.com/tauri-apps/muda/blob/dev/examples/wry.rs
// if cfg.enable_default_menu_bar { // if cfg.enable_default_menu_bar {
@@ -58,6 +98,7 @@ pub(crate) fn build(
move |request, responder| { move |request, responder| {
let custom_head = custom_head.clone(); let custom_head = custom_head.clone();
let index_file = index_file.clone(); let index_file = index_file.clone();
let assets_head = assets_head.clone();
let root_name = root_name.clone(); let root_name = root_name.clone();
let asset_handlers_ref = asset_handlers_ref.clone(); let asset_handlers_ref = asset_handlers_ref.clone();
let edit_queue = edit_queue.clone(); let edit_queue = edit_queue.clone();
@@ -66,6 +107,7 @@ pub(crate) fn build(
request, request,
custom_head.clone(), custom_head.clone(),
index_file.clone(), index_file.clone(),
assets_head.clone(),
&root_name, &root_name,
&asset_handlers_ref, &asset_handlers_ref,
&edit_queue, &edit_queue,
+1
View File
@@ -16,6 +16,7 @@ dioxus-html = { workspace = true, optional = true }
dioxus-core-macro = { workspace = true, optional = true } dioxus-core-macro = { workspace = true, optional = true }
dioxus-hooks = { workspace = true, optional = true } dioxus-hooks = { workspace = true, optional = true }
dioxus-rsx = { workspace = true, optional = true } dioxus-rsx = { workspace = true, optional = true }
manganis = { git = "https://github.com/DioxusLabs/collect-assets" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
dioxus-hot-reload = { workspace = true, optional = true } dioxus-hot-reload = { workspace = true, optional = true }
+3
View File
@@ -22,6 +22,9 @@ pub use dioxus_rsx as rsx;
pub use dioxus_core_macro as core_macro; pub use dioxus_core_macro as core_macro;
pub mod prelude { pub mod prelude {
pub use manganis;
pub use manganis::mg;
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
pub use crate::hooks::*; pub use crate::hooks::*;
+4
View File
@@ -70,6 +70,10 @@ dioxus-hot-reload = { workspace = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] } web-sys = { version = "0.3.61", features = ["Window", "Document", "Element", "HtmlDocument", "Storage", "console"] }
[target.'cfg(any(target_os = "windows",target_os = "macos",target_os = "linux",target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))'.dependencies]
# This is only for debug mode, and it appears mobile does not support some packages this uses
manganis-cli-support = { git = "https://github.com/DioxusLabs/collect-assets", features = ["webp", "html"] }
[features] [features]
default = ["hot-reload", "default-tls"] default = ["hot-reload", "default-tls"]
router = ["dioxus-router"] router = ["dioxus-router"]
@@ -1,3 +1,4 @@
dist dist
target target
static static
.dioxus
@@ -1,2 +1,4 @@
dist dist
target target
static
.dioxus
@@ -1,3 +1,4 @@
dist dist
target target
static static
.dioxus
@@ -1,3 +1,4 @@
dist dist
target target
static static
.dioxus
@@ -1,3 +1,4 @@
dist dist
target target
static static
.dioxus
@@ -1,3 +1,4 @@
docs docs
target target
static static
.dioxus
@@ -1,3 +1,4 @@
dist dist
target target
static static
.dioxus
@@ -276,6 +276,9 @@ where
fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self { fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self {
use tower_http::services::{ServeDir, ServeFile}; use tower_http::services::{ServeDir, ServeFile};
// Copy over any assets we find
crate::collect_assets::copy_assets();
let assets_path = assets_path.into(); let assets_path = assets_path.into();
// Serve all files in dist folder except index.html // Serve all files in dist folder except index.html
@@ -241,6 +241,9 @@ impl DioxusRouterExt for Router {
} }
fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self { fn serve_static_assets(mut self, assets_path: impl Into<std::path::PathBuf>) -> Self {
// Copy over any assets we find
crate::collect_assets::copy_assets();
let assets_path = assets_path.into(); let assets_path = assets_path.into();
// Serve all files in dist folder except index.html // Serve all files in dist folder except index.html
@@ -187,6 +187,9 @@ pub fn serve_dioxus_application<P: Clone + serde::Serialize + Send + Sync + 'sta
// Serve the dist folder and the index.html file // Serve the dist folder and the index.html file
let serve_dir = warp::fs::dir(cfg.assets_path); let serve_dir = warp::fs::dir(cfg.assets_path);
// Copy over any assets we find
crate::collect_assets::copy_assets();
connect_hot_reload() connect_hot_reload()
// First register the server functions // First register the server functions
.or(register_server_fns(server_fn_route)) .or(register_server_fns(server_fn_route))
+61
View File
@@ -0,0 +1,61 @@
#[cfg(any(feature = "axum", feature = "warp", feature = "salvo"))]
pub fn copy_assets() {
#[cfg(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
))]
{
// The CLI will copy assets to the current working directory
if std::env::var_os("DIOXUS_ACTIVE").is_some() {
return;
}
use manganis_cli_support::AssetManifest;
use manganis_cli_support::AssetManifestExt;
use manganis_cli_support::Config;
use std::path::PathBuf;
let config = Config::current();
let asset_location = config.assets_serve_location();
let asset_location = PathBuf::from(asset_location);
let _ = std::fs::remove_dir_all(&asset_location);
println!("Finding assets... (Note: if you run a dioxus desktop application with the CLI. This process will be significantly faster.)");
let manifest = AssetManifest::load();
let has_assets = manifest
.packages()
.iter()
.any(|package| !package.assets().is_empty());
if has_assets {
println!("Copying and optimizing assets...");
manifest.copy_static_assets_to(&asset_location).unwrap();
println!("Copied assets to {}", asset_location.display());
} else {
println!("No assets found");
}
}
#[cfg(not(all(
debug_assertions,
any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)
)))]
{
println!(
"Skipping assets in release mode. You compile assets with the dioxus-cli in release mode"
);
}
}
+1
View File
@@ -14,6 +14,7 @@ pub mod router;
mod adapters; mod adapters;
#[cfg(feature = "ssr")] #[cfg(feature = "ssr")]
pub use adapters::*; pub use adapters::*;
mod collect_assets;
mod hooks; mod hooks;
#[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))] #[cfg(all(debug_assertions, feature = "hot-reload", feature = "ssr"))]
mod hot_reload; mod hot_reload;
+1 -1
View File
@@ -155,7 +155,7 @@ pub fn init<Ctx: HotReloadingContext + Send + 'static>(cfg: Config<Ctx>) {
let target_dir = crate_dir.join("target"); let target_dir = crate_dir.join("target");
let hot_reload_socket_path = target_dir.join("dioxusin"); let hot_reload_socket_path = target_dir.join("dioxusin");
#[cfg(target_os = "macos")] #[cfg(unix)]
{ {
// On unix, if you force quit the application, it can leave the file socket open // On unix, if you force quit the application, it can leave the file socket open
// This will cause the local socket listener to fail to open // This will cause the local socket listener to fail to open
+5 -5
View File
@@ -76,23 +76,23 @@ module.exports = defineConfig({
command: command:
"cargo run --package dioxus-playwright-liveview-test --bin dioxus-playwright-liveview-test", "cargo run --package dioxus-playwright-liveview-test --bin dioxus-playwright-liveview-test",
port: 3030, port: 3030,
timeout: 10 * 60 * 1000, timeout: 20 * 60 * 1000,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
stdout: "pipe", stdout: "pipe",
}, },
{ {
cwd: path.join(process.cwd(), "web"), cwd: path.join(process.cwd(), "web"),
command: "cargo run --package dioxus-cli -- serve", command: "cargo run --package dioxus-cli --release -- serve --skip-assets",
port: 8080, port: 8080,
timeout: 10 * 60 * 1000, timeout: 20 * 60 * 1000,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
stdout: "pipe", stdout: "pipe",
}, },
{ {
cwd: path.join(process.cwd(), 'fullstack'), cwd: path.join(process.cwd(), 'fullstack'),
command: 'cargo run --package dioxus-cli -- build --features web --release && cargo run --release --features ssr', command: 'cargo run --package dioxus-cli --release -- build --features web --release --skip-assets\ncargo run --release --features ssr',
port: 3333, port: 3333,
timeout: 10 * 60 * 1000, timeout: 20 * 60 * 1000,
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
stdout: "pipe", stdout: "pipe",
}, },