diff --git a/Cargo.lock b/Cargo.lock index 122dbd6407..1a40eac30f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7524,6 +7524,7 @@ checksum = "20c9e1f991b3861d25bf872ecca2eb6a73f7a9fe671da047cd1f9b49c65cbc40" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", + "browserslist-rs", "const-str", "cssparser 0.33.0", "cssparser-color", @@ -7541,6 +7542,7 @@ dependencies = [ "rayon", "serde", "smallvec", + "static-self", ] [[package]] @@ -8894,6 +8896,7 @@ dependencies = [ "precomputed-hash", "rustc-hash 2.1.0", "smallvec", + "static-self", ] [[package]] @@ -11779,6 +11782,27 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "static-self" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253e76c8c993a7b1b201b0539228b334582153cd4364292822d2c30776d469c7" +dependencies = [ + "smallvec", + "static-self-derive", +] + +[[package]] +name = "static-self-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5268c96d4b907c558a9a52d8492522d6c7b559651a5e1d8f2d551e461b9425d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/packages/cli-opt/Cargo.toml b/packages/cli-opt/Cargo.toml index 8d5e0cc67f..6ba790c3d1 100644 --- a/packages/cli-opt/Cargo.toml +++ b/packages/cli-opt/Cargo.toml @@ -32,7 +32,7 @@ png = "0.17.9" image = { version = "0.25", features = ["avif"] } # CSS Minification -lightningcss = "1.0.0-alpha.60" +lightningcss = { version = "1.0.0-alpha.60", features = ["browserslist", "into_owned"] } # SCSS Processing grass = "0.13.4" diff --git a/packages/cli-opt/src/css.rs b/packages/cli-opt/src/css.rs index 6dcc37f15e..93405df376 100644 --- a/packages/cli-opt/src/css.rs +++ b/packages/cli-opt/src/css.rs @@ -6,9 +6,9 @@ use grass::OutputStyle; use lightningcss::{ printer::PrinterOptions, stylesheet::{MinifyOptions, ParserOptions, StyleSheet}, + targets::{Browsers, Targets}, }; use manganis_core::CssAssetOptions; -use tracing::{debug, warn}; pub(crate) fn process_css( css_options: &CssAssetOptions, @@ -18,7 +18,17 @@ pub(crate) fn process_css( let css = std::fs::read_to_string(source)?; let css = if css_options.minified() { - minify_css(&css) + // Try to minify the css. If we fail, log the error and use the unminified css + match minify_css(&css) { + Ok(minified) => minified, + Err(err) => { + tracing::error!( + "Failed to minify css; Falling back to unminified css. Error: {}", + err + ); + css + } + } } else { css }; @@ -33,15 +43,39 @@ pub(crate) fn process_css( Ok(()) } -pub(crate) fn minify_css(css: &str) -> String { - let mut stylesheet = StyleSheet::parse(css, ParserOptions::default()).unwrap(); - stylesheet.minify(MinifyOptions::default()).unwrap(); +pub(crate) fn minify_css(css: &str) -> anyhow::Result { + let options = ParserOptions { + error_recovery: true, + ..Default::default() + }; + let mut stylesheet = StyleSheet::parse(css, options).map_err(|err| err.into_owned())?; + + // We load the browser list from the standard browser list file or use the browserslist default if we don't find any + // settings. Without the browser lists default, lightningcss will default to supporting only the newest versions of + // browsers. + let browsers_list = match Browsers::load_browserslist()? { + Some(browsers) => Some(browsers), + None => { + Browsers::from_browserslist(["defaults"]).expect("borwserslists should have defaults") + } + }; + + let targets = Targets { + browsers: browsers_list, + ..Default::default() + }; + + stylesheet.minify(MinifyOptions { + targets, + ..Default::default() + })?; let printer = PrinterOptions { + targets, minify: true, ..Default::default() }; - let res = stylesheet.to_css(printer).unwrap(); - res.code + let res = stylesheet.to_css(printer)?; + Ok(res.code) } /// Process an scss/sass file into css. @@ -61,7 +95,7 @@ pub(crate) fn process_scss( .logger(&ScssLogger {}); let css = grass::from_path(source, &options)?; - let minified = minify_css(&css); + let minified = minify_css(&css)?; std::fs::write(output_path, minified).with_context(|| { format!( @@ -79,7 +113,7 @@ struct ScssLogger {} impl grass::Logger for ScssLogger { fn debug(&self, location: SpanLoc, message: &str) { - debug!( + tracing::debug!( "{}:{} DEBUG: {}", location.file.name(), location.begin.line + 1, @@ -88,7 +122,7 @@ impl grass::Logger for ScssLogger { } fn warn(&self, location: SpanLoc, message: &str) { - warn!( + tracing::warn!( "Warning: {}\n ./{}:{}:{}", message, location.file.name(), diff --git a/packages/cli/src/serve/handle.rs b/packages/cli/src/serve/handle.rs index 1a722d639f..b2cbcefbf4 100644 --- a/packages/cli/src/serve/handle.rs +++ b/packages/cli/src/serve/handle.rs @@ -1,5 +1,6 @@ use crate::{AppBundle, Platform, Result}; use anyhow::Context; +use dioxus_cli_opt::process_file_to; use std::{ fs, net::SocketAddr, @@ -195,7 +196,14 @@ impl AppHandle { // The asset might've been renamed thanks to the manifest, let's attempt to reload that too if let Some(resource) = self.app.app.assets.assets.get(&changed_file).as_ref() { - let res = std::fs::copy(&changed_file, asset_dir.join(resource.bundled_path())); + let output_path = asset_dir.join(resource.bundled_path()); + // Remove the old asset if it exists + _ = std::fs::remove_file(&output_path); + // And then process the asset with the options into the **old** asset location. If we recompiled, + // the asset would be in a new location because the contents and hash have changed. Since we are + // hotreloading, we need to use the old asset location it was originally written to. + let options = *resource.options(); + let res = process_file_to(&options, &changed_file, &output_path); bundled_name = Some(PathBuf::from(resource.bundled_path())); if let Err(e) = res { tracing::debug!("Failed to hotreload asset {e}");