diff --git a/Cargo.lock b/Cargo.lock index 086673b7a..4c4720564 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -396,9 +396,9 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36" dependencies = [ "camino", "cargo-platform", @@ -590,6 +590,21 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "console" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -872,7 +887,7 @@ dependencies = [ "headers", "html_parser", "hyper", - "loading", + "indicatif", "log", "notify", "proc-macro2", @@ -1101,6 +1116,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.31" @@ -1906,6 +1927,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017d0ce94b8e91e29d2c78ed891e57e5ec3dc4371820a9d96abab4af09eb8ad" +dependencies = [ + "console", + "number_prefix", + "unicode-width", +] + [[package]] name = "inotify" version = "0.9.6" @@ -2145,12 +2177,6 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -[[package]] -name = "loading" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "676420b03272176423578148eee61319e893422694f6d96a394841cb0901357f" - [[package]] name = "lock_api" version = "0.4.7" @@ -2469,6 +2495,12 @@ dependencies = [ "libc", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "objc" version = "0.2.7" @@ -3525,6 +3557,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "textwrap" version = "0.15.0" @@ -3893,6 +3935,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 7e982bc78..da2467fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,14 +29,14 @@ notify = { version = "5.0.0-pre.13", features = ["serde"] } html_parser = "0.6.2" binary-install = "0.0.2" convert_case = "0.5.0" -cargo_metadata = "0.14.1" +cargo_metadata = "0.15.0" tokio = { version = "1.16.1", features = ["full"] } atty = "0.2.14" regex = "1.5.4" chrono = "0.4.19" anyhow = "1.0.53" hyper = "0.14.17" -loading = "0.3.0" +indicatif = "0.17.0-rc.11" axum = { version = "0.5.1", features = ["ws", "headers"] } tower-http = { version = "0.2.2", features = ["fs", "trace"] } diff --git a/src/builder.rs b/src/builder.rs index 5ef084326..d164e5418 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -4,19 +4,26 @@ use crate::{ tools::Tool, DioxusConfig, }; -use cargo_metadata::Message; -use loading::Loading; +use cargo_metadata::{diagnostic::Diagnostic, Message}; +use indicatif::{ProgressBar, ProgressStyle}; +use serde::Serialize; use std::{ - collections::HashMap, fs::{copy, create_dir_all, remove_dir_all, File}, io::Read, panic, path::PathBuf, process::Command, + time::Duration, }; use wasm_bindgen_cli_support::Bindgen; -pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { +#[derive(Serialize, Debug, Clone)] +pub struct BuildResult { + pub warnings: Vec, + pub elapsed_time: u128, +} + +pub fn build(config: &CrateConfig, quiet: bool) -> Result { // [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 // [3] Wasm-bindgen the .wasm fiile, and move it into the {builddir}/modules/xxxx/xxxx_bg.wasm @@ -78,13 +85,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { ExecutableType::Example(name) => cmd.arg("--example").arg(name), }; - prettier_build(cmd).unwrap(); - - // if !output.status.success() { - // log::error!("Build failed!"); - // let reason = String::from_utf8_lossy(&output.stderr).to_string(); - // return Err(Error::BuildFailed(reason)); - // } + let warning_messages = prettier_build(cmd)?; // [2] Establish the output directory structure let bindgen_outdir = out_dir.join("assets").join("dioxus"); @@ -122,8 +123,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { .unwrap(); }); if bindgen_result.is_err() { - log::error!("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate."); - return Ok(()); + return Err(Error::BuildFailed("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate.".to_string())); } // check binaryen:wasm-opt tool @@ -193,8 +193,10 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<()> { } let t_end = std::time::Instant::now(); - log::info!("🏁 Done in {}ms!", (t_end - t_start).as_millis()); - Ok(()) + Ok(BuildResult { + warnings: warning_messages, + elapsed_time: (t_end - t_start).as_millis() + }) } pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { @@ -327,27 +329,46 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> { Ok(()) } -fn prettier_build(mut cmd: Command) -> anyhow::Result<()> { - let loading = Loading::default(); - let mut record: HashMap = HashMap::new(); +fn prettier_build(mut cmd: Command) -> anyhow::Result> { + let mut warning_messages: Vec = vec![]; + + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(200)); + pb.set_style( + ProgressStyle::with_template("{spinner:.dim.bold} {wide_msg}") + .unwrap() + .tick_chars("/|\\- "), + ); + pb.set_message("Start to build project"); + let mut command = cmd.spawn()?; let reader = std::io::BufReader::new(command.stdout.take().unwrap()); for message in cargo_metadata::Message::parse_stream(reader) { match message.unwrap() { - Message::CompilerMessage(_msg) => { - // println!("{:#?}", msg); + Message::CompilerMessage(msg) => { + let message = msg.message; + match message.level { + cargo_metadata::diagnostic::DiagnosticLevel::Error => { + log::error!("Build Failed: {}", message.message); + std::process::exit(1); + } + cargo_metadata::diagnostic::DiagnosticLevel::Warning => { + warning_messages.push(message.clone()); + } + _ => {} + } } Message::CompilerArtifact(artifact) => { - record.insert(artifact.package_id.to_string(), artifact.target.name); + pb.set_message(format!("Compiling {} ", artifact.package_id)); + pb.tick(); } Message::BuildScriptExecuted(script) => { - let package_id = script.package_id.to_string(); - if let Some(name) = record.get(&package_id) { - loading.text(format!("[{}] build done", name)); - } + let _package_id = script.package_id.to_string(); } Message::BuildFinished(finished) => { if finished.success { + pb.finish_and_clear(); + println!("Done in"); } else { std::process::exit(1); } @@ -355,7 +376,7 @@ fn prettier_build(mut cmd: Command) -> anyhow::Result<()> { _ => (), // Unknown message } } - Ok(()) + Ok(warning_messages) } pub fn gen_page(config: &DioxusConfig, serve: bool) -> String { diff --git a/src/cli/serve/mod.rs b/src/cli/serve/mod.rs index 62843d653..93770b6ed 100644 --- a/src/cli/serve/mod.rs +++ b/src/cli/serve/mod.rs @@ -38,31 +38,25 @@ impl Serve { .clone() }); - match platform.as_str() { - "web" => { - crate::builder::build(&crate_config, false)?; - } - "desktop" => { - crate::builder::build_desktop(&crate_config, true)?; + if platform.as_str() == "desktop" { + crate::builder::build_desktop(&crate_config, true)?; - match &crate_config.executable { - crate::ExecutableType::Binary(name) - | crate::ExecutableType::Lib(name) - | crate::ExecutableType::Example(name) => { - let mut file = crate_config.out_dir.join(name); - if cfg!(windows) { - file.set_extension("exe"); - } - Command::new(crate_config.out_dir.join(file).to_str().unwrap()) - .stdout(Stdio::inherit()) - .output()?; + match &crate_config.executable { + crate::ExecutableType::Binary(name) + | crate::ExecutableType::Lib(name) + | crate::ExecutableType::Example(name) => { + let mut file = crate_config.out_dir.join(name); + if cfg!(windows) { + file.set_extension("exe"); } + Command::new(crate_config.out_dir.join(file).to_str().unwrap()) + .stdout(Stdio::inherit()) + .output()?; } - return Ok(()); - } - _ => { - return custom_error!("Unsoppurt platform target."); } + return Ok(()); + } else if platform != "web" { + return custom_error!("Unsoppurt platform target."); } // generate dev-index page diff --git a/src/server/mod.rs b/src/server/mod.rs index 3ecce6c26..b20d84db3 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -6,6 +6,7 @@ use axum::{ routing::{get, get_service}, Router, }; +use cargo_metadata::diagnostic::Diagnostic; use colored::Colorize; use dioxus::rsx_interpreter::SetRsxMessage; use notify::{RecommendedWatcher, Watcher}; @@ -15,7 +16,7 @@ use std::{net::UdpSocket, path::PathBuf, process::Command, sync::Arc}; use tower::ServiceBuilder; use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody}; -use crate::{builder, serve::Serve, CrateConfig, Result}; +use crate::{builder, serve::Serve, BuildResult, CrateConfig, Result}; use tokio::sync::broadcast; mod hot_reload; @@ -27,9 +28,9 @@ pub struct BuildManager { } impl BuildManager { - fn rebuild(&self) -> Result<()> { + fn rebuild(&self) -> Result { log::info!("🪁 Rebuild project"); - builder::build(&self.config, true)?; + let result = builder::build(&self.config, true)?; // change the websocket reload state to true; // the page will auto-reload. if self @@ -43,7 +44,7 @@ impl BuildManager { let _ = Serve::regen_dev_page(&self.config); } let _ = self.reload_tx.send(()); - Ok(()) + Ok(result) } } @@ -61,6 +62,8 @@ pub async fn startup(port: u16, config: CrateConfig) -> Result<()> { } pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { + let first_build_result = crate::builder::build(&config, false)?; + log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -159,9 +162,22 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } } if needs_rebuild { - print_console_info(port, &config, evt.paths); - if let Err(err) = build_manager.rebuild() { - log::error!("{}", err); + match build_manager.rebuild() { + Ok(res) => { + print_console_info( + port, + &config, + PrettierOptions { + changed: evt.paths, + warnings: res.warnings, + elapsed_time: res.elapsed_time, + }, + ); + } + Err(err) => { + log::error!("{}", err); + std::process::exit(1); + } } } if !messages.is_empty() { @@ -183,7 +199,15 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0:8080 - print_console_info(port, &config, vec![]); + print_console_info( + port, + &config, + PrettierOptions { + changed: vec![], + warnings: first_build_result.warnings, + elapsed_time: first_build_result.elapsed_time, + }, + ); let file_service_config = config.clone(); let file_service = ServiceBuilder::new() @@ -246,6 +270,8 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> { } pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { + let first_build_result = crate::builder::build(&config, false)?; + log::info!("🚀 Starting development server..."); let dist_path = config.out_dir.clone(); @@ -276,10 +302,20 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { let mut watcher = RecommendedWatcher::new(move |info: notify::Result| { let config = watcher_config.clone(); if info.is_ok() { - print_console_info(port, &config, info.unwrap().paths); if chrono::Local::now().timestamp() > last_update_time { match build_manager.rebuild() { - Ok(_) => last_update_time = chrono::Local::now().timestamp(), + Ok(res) => { + last_update_time = chrono::Local::now().timestamp(); + print_console_info( + port, + &config, + PrettierOptions { + changed: info.unwrap().paths, + warnings: res.warnings, + elapsed_time: res.elapsed_time, + }, + ); + } Err(e) => log::error!("{}", e), } } @@ -297,8 +333,16 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { } // start serve dev-server at 0.0.0.0 - print_console_info(port, &config, vec![]); - + print_console_info( + port, + &config, + PrettierOptions { + changed: vec![], + warnings: first_build_result.warnings, + elapsed_time: first_build_result.elapsed_time, + }, + ); + let file_service_config = config.clone(); let file_service = ServiceBuilder::new() .and_then( @@ -355,7 +399,14 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> { Ok(()) } -fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { +#[derive(Debug, Default)] +pub struct PrettierOptions { + changed: Vec, + warnings: Vec, + elapsed_time: u128, +} + +fn print_console_info(port: u16, config: &CrateConfig, options: PrettierOptions) { print!( "{}", String::from_utf8_lossy( @@ -413,7 +464,7 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { "False" }; - if changed.len() <= 0 { + if options.changed.len() <= 0 { println!( "{} @ v{}\n", "Dioxus".bold().green(), @@ -422,7 +473,9 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { } else { println!( "Project Reloaded: {}\n", - format!("Changed {} files.", changed.len()).purple().bold() + format!("Changed {} files.", options.changed.len()) + .purple() + .bold() ); } println!( @@ -446,12 +499,11 @@ fn print_console_info(port: u16, config: &CrateConfig, changed: Vec) { println!("\t> URL Rewrite [index_on_404] : {}", url_rewrite.purple()); println!(""); - if changed.len() <= 0 { + if options.changed.len() <= 0 { log::info!("{}", "Server startup completed.".green().bold()); } else { log::info!("{}", "Project rebuild completed.".green().bold()); } - } fn get_ip() -> Option {