diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 49147225c..6b1b5acf0 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -44,7 +44,7 @@ jobs: # args: --path packages/cli - name: Run Playwright tests run: npx playwright test - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: name: playwright-report diff --git a/README.md b/README.md index d1ec98019..65cd08263 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ So... Dioxus is great, but why won't it work for me? ## Contributing - Check out the website [section on contributing](https://dioxuslabs.com/learn/0.4/contributing). - Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues). -- Join the discord and ask questions! +- [Join](https://discord.gg/XgGxMSkvUM) the discord and ask questions! diff --git a/packages/cli/Cargo.toml b/packages/cli/Cargo.toml index 2cc19f770..4335faa22 100644 --- a/packages/cli/Cargo.toml +++ b/packages/cli/Cargo.toml @@ -85,6 +85,7 @@ dioxus-html = { workspace = true, features = ["hot-reload-context"] } dioxus-core = { workspace = true, features = ["serialize"] } dioxus-hot-reload = { workspace = true } interprocess-docfix = { version = "1.2.2" } +gitignore = "1.0.8" [features] default = [] diff --git a/packages/cli/README.md b/packages/cli/README.md index 8379f22cc..2325d6784 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -10,7 +10,7 @@ It handles building, bundling, development and publishing to simplify developmen ### Install the stable version (recommended) -``` +```shell cargo install dioxus-cli ``` @@ -20,7 +20,7 @@ To get the latest bug fixes and features, you can install the development versio However, this is not fully tested. That means you're probably going to have more bugs despite having the latest bug fixes. -``` +```shell cargo install --git https://github.com/DioxusLabs/dioxus dioxus-cli ``` @@ -29,7 +29,7 @@ and install it in Cargo's global binary directory (`~/.cargo/bin/` by default). ### Install from local folder -``` +```shell cargo install --path . --debug ``` @@ -40,7 +40,7 @@ It will be cloned from the [dioxus-template](https://github.com/DioxusLabs/dioxu Alternatively, you can specify the template path: -``` +```shell dx create hello --template gh:dioxuslabs/dioxus-template ``` diff --git a/packages/cli/src/builder.rs b/packages/cli/src/builder.rs index 3e27a603c..4e073f4df 100644 --- a/packages/cli/src/builder.rs +++ b/packages/cli/src/builder.rs @@ -274,7 +274,6 @@ pub fn build_desktop( let mut cmd = subprocess::Exec::cmd("cargo") .cwd(&config.crate_dir) .arg("build") - .arg("--quiet") .arg("--message-format=json"); if config.release { @@ -282,6 +281,8 @@ pub fn build_desktop( } if config.verbose { cmd = cmd.arg("--verbose"); + } else { + cmd = cmd.arg("--quiet"); } if config.custom_profile.is_some() { diff --git a/packages/cli/src/cli/autoformat.rs b/packages/cli/src/cli/autoformat.rs index 6c1ce04a1..812a4c325 100644 --- a/packages/cli/src/cli/autoformat.rs +++ b/packages/cli/src/cli/autoformat.rs @@ -27,15 +27,17 @@ pub struct Autoformat { impl Autoformat { // Todo: autoformat the entire crate pub async fn autoformat(self) -> Result<()> { + let Autoformat { check, raw, file } = self; + // Default to formatting the project - if self.raw.is_none() && self.file.is_none() { - if let Err(e) = autoformat_project(self.check).await { + if raw.is_none() && file.is_none() { + if let Err(e) = autoformat_project(check).await { eprintln!("error formatting project: {}", e); exit(1); } } - if let Some(raw) = self.raw { + if let Some(raw) = raw { let indent = indentation_for(".")?; if let Some(inner) = dioxus_autofmt::fmt_block(&raw, 0, indent) { println!("{}", inner); @@ -47,47 +49,90 @@ impl Autoformat { } // Format single file - if let Some(file) = self.file { - let file_content; - let indent; - if file == "-" { - indent = indentation_for(".")?; - let mut contents = String::new(); - std::io::stdin().read_to_string(&mut contents)?; - file_content = Ok(contents); - } else { - indent = indentation_for(".")?; - file_content = fs::read_to_string(&file); - }; - - match file_content { - Ok(s) => { - let edits = dioxus_autofmt::fmt_file(&s, indent); - let out = dioxus_autofmt::apply_formats(&s, edits); - if file == "-" { - print!("{}", out); - } else { - match fs::write(&file, out) { - Ok(_) => { - println!("formatted {}", file); - } - Err(e) => { - eprintln!("failed to write formatted content to file: {}", e); - } - } - } - } - Err(e) => { - eprintln!("failed to open file: {}", e); - exit(1); - } - } + if let Some(file) = file { + refactor_file(file)?; } Ok(()) } } +fn refactor_file(file: String) -> Result<(), Error> { + let indent = indentation_for(".")?; + let file_content = if file == "-" { + let mut contents = String::new(); + std::io::stdin().read_to_string(&mut contents)?; + Ok(contents) + } else { + fs::read_to_string(&file) + }; + let Ok(s) = file_content else { + eprintln!("failed to open file: {}", file_content.unwrap_err()); + exit(1); + }; + let edits = dioxus_autofmt::fmt_file(&s, indent); + let out = dioxus_autofmt::apply_formats(&s, edits); + + if file == "-" { + print!("{}", out); + } else if let Err(e) = fs::write(&file, out) { + eprintln!("failed to write formatted content to file: {e}",); + } else { + println!("formatted {}", file); + } + + Ok(()) +} + +fn get_project_files(config: &CrateConfig) -> Vec { + let mut files = vec![]; + + let gitignore_path = config.crate_dir.join(".gitignore"); + if gitignore_path.is_file() { + let gitigno = gitignore::File::new(gitignore_path.as_path()).unwrap(); + if let Ok(git_files) = gitigno.included_files() { + let git_files = git_files + .into_iter() + .filter(|f| f.ends_with(".rs") && !is_target_dir(f)); + files.extend(git_files) + }; + } else { + collect_rs_files(&config.crate_dir, &mut files); + } + + files +} + +fn is_target_dir(file: &Path) -> bool { + let stripped = if let Ok(cwd) = std::env::current_dir() { + file.strip_prefix(cwd).unwrap_or(file) + } else { + file + }; + if let Some(first) = stripped.components().next() { + first.as_os_str() == "target" + } else { + false + } +} + +async fn format_file( + path: impl AsRef, + indent: IndentOptions, +) -> Result { + let contents = tokio::fs::read_to_string(&path).await?; + + let edits = dioxus_autofmt::fmt_file(&contents, indent); + let len = edits.len(); + + if !edits.is_empty() { + let out = dioxus_autofmt::apply_formats(&contents, edits); + tokio::fs::write(path, out).await?; + } + + Ok(len) +} + /// Read every .rs file accessible when considering the .gitignore and try to format it /// /// Runs using Tokio for multithreading, so it should be really really fast @@ -96,8 +141,7 @@ impl Autoformat { async fn autoformat_project(check: bool) -> Result<()> { let crate_config = crate::CrateConfig::new(None)?; - let mut files_to_format = vec![]; - collect_rs_files(&crate_config.crate_dir, &mut files_to_format); + let files_to_format = get_project_files(&crate_config); if files_to_format.is_empty() { return Ok(()); @@ -107,38 +151,17 @@ async fn autoformat_project(check: bool) -> Result<()> { let counts = files_to_format .into_iter() - .filter(|file| { - if file.components().any(|f| f.as_os_str() == "target") { - return false; - } - - true - }) .map(|path| async { - let _path = path.clone(); - let _indent = indent.clone(); - let res = tokio::spawn(async move { - let contents = tokio::fs::read_to_string(&path).await?; - - let edits = dioxus_autofmt::fmt_file(&contents, _indent.clone()); - let len = edits.len(); - - if !edits.is_empty() { - let out = dioxus_autofmt::apply_formats(&contents, edits); - tokio::fs::write(&path, out).await?; - } - - Ok(len) as Result - }) - .await; + let path_clone = path.clone(); + let res = tokio::spawn(format_file(path, indent.clone())).await; match res { Err(err) => { - eprintln!("error formatting file: {}\n{err}", _path.display()); + eprintln!("error formatting file: {}\n{err}", path_clone.display()); None } Ok(Err(err)) => { - eprintln!("error formatting file: {}\n{err}", _path.display()); + eprintln!("error formatting file: {}\n{err}", path_clone.display()); None } Ok(Ok(res)) => Some(res), @@ -148,13 +171,7 @@ async fn autoformat_project(check: bool) -> Result<()> { .collect::>() .await; - let files_formatted: usize = counts - .into_iter() - .map(|f| match f { - Some(res) => res, - _ => 0, - }) - .sum(); + let files_formatted: usize = counts.into_iter().flatten().sum(); if files_formatted > 0 && check { eprintln!("{} files needed formatting", files_formatted); @@ -207,26 +224,24 @@ fn indentation_for(file_or_dir: impl AsRef) -> Result { )) } -fn collect_rs_files(folder: &Path, files: &mut Vec) { - let Ok(folder) = folder.read_dir() else { +fn collect_rs_files(folder: &impl AsRef, files: &mut Vec) { + if is_target_dir(folder.as_ref()) { + return; + } + let Ok(folder) = folder.as_ref().read_dir() else { return; }; - // load the gitignore - for entry in folder { let Ok(entry) = entry else { continue; }; - let path = entry.path(); - if path.is_dir() { collect_rs_files(&path, files); } - if let Some(ext) = path.extension() { - if ext == "rs" { + if ext == "rs" && !is_target_dir(&path) { files.push(path); } } diff --git a/packages/cli/src/main.rs b/packages/cli/src/main.rs index 7ca1b2c9a..2358551f2 100644 --- a/packages/cli/src/main.rs +++ b/packages/cli/src/main.rs @@ -42,34 +42,36 @@ async fn main() -> anyhow::Result<()> { set_up_logging(); - let bin = get_bin(args.bin)?; + let bin = get_bin(args.bin); - let _dioxus_config = DioxusConfig::load(Some(bin.clone())) + if let Ok(bin) = &bin { + let _dioxus_config = DioxusConfig::load(Some(bin.clone())) .map_err(|e| anyhow!("Failed to load Dioxus config because: {e}"))? .unwrap_or_else(|| { log::warn!("You appear to be creating a Dioxus project from scratch; we will use the default config"); DioxusConfig::default() }); - #[cfg(feature = "plugin")] - PluginManager::init(_dioxus_config.plugin) - .map_err(|e| anyhow!("🚫 Plugin system initialization failed: {e}"))?; + #[cfg(feature = "plugin")] + PluginManager::init(_dioxus_config.plugin) + .map_err(|e| anyhow!("🚫 Plugin system initialization failed: {e}"))?; + } match args.action { Translate(opts) => opts .translate() .map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)), - Build(opts) => opts - .build(Some(bin.clone())) + Build(opts) if bin.is_ok() => opts + .build(Some(bin.unwrap().clone())) .map_err(|e| anyhow!("🚫 Building project failed: {}", e)), - Clean(opts) => opts - .clean(Some(bin.clone())) + Clean(opts) if bin.is_ok() => opts + .clean(Some(bin.unwrap().clone())) .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)), - Serve(opts) => opts - .serve(Some(bin.clone())) + Serve(opts) if bin.is_ok() => opts + .serve(Some(bin.unwrap().clone())) .await .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)), @@ -81,8 +83,8 @@ async fn main() -> anyhow::Result<()> { .config() .map_err(|e| anyhow!("🚫 Configuring new project failed: {}", e)), - Bundle(opts) => opts - .bundle(Some(bin.clone())) + Bundle(opts) if bin.is_ok() => opts + .bundle(Some(bin.unwrap().clone())) .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)), #[cfg(feature = "plugin")] @@ -107,5 +109,6 @@ async fn main() -> anyhow::Result<()> { Ok(()) } + _ => Err(anyhow::anyhow!(bin.unwrap_err())), } } diff --git a/packages/cli/src/server/desktop/mod.rs b/packages/cli/src/server/desktop/mod.rs index a40667696..fe78240b4 100644 --- a/packages/cli/src/server/desktop/mod.rs +++ b/packages/cli/src/server/desktop/mod.rs @@ -147,7 +147,11 @@ async fn start_desktop_hot_reload(hot_reload_state: HotReloadState) -> Result<() println!("Connected to hot reloading 🚀"); } Err(err) => { - if err.kind() != std::io::ErrorKind::WouldBlock { + let error_string = err.to_string(); + // Filter out any error messages about a operation that may block and an error message that triggers on some operating systems that says "Waiting for a process to open the other end of the pipe" without WouldBlock being set + let display_error = err.kind() != std::io::ErrorKind::WouldBlock + && !error_string.contains("Waiting for a process"); + if display_error { println!("Error connecting to hot reloading: {} (Hot reloading is a feature of the dioxus-cli. If you are not using the CLI, this error can be ignored)", err); } } diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index 35eb9c5f1..b2a8e94c8 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -42,6 +42,7 @@ features = [ "HtmlFormElement", "Text", "Window", + "console", ] [features] diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index f45d6558a..b8e9255f7 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -45,7 +45,16 @@ impl WebsysDom { let document = load_document(); let root = match document.get_element_by_id(&cfg.rootname) { Some(root) => root, - None => document.create_element("body").ok().unwrap(), + None => { + web_sys::console::error_1( + &format!( + "element '#{}' not found. mounting to the body.", + cfg.rootname + ) + .into(), + ); + document.create_element("body").ok().unwrap() + } }; let interpreter = Channel::default();