Quellcode durchsuchen

Use a better default browser list and fail gracefully when css parsing fails (#3509)

Evan Almloff vor 5 Monaten
Ursprung
Commit
807cfd29b7
4 geänderte Dateien mit 78 neuen und 12 gelöschten Zeilen
  1. 24 0
      Cargo.lock
  2. 1 1
      packages/cli-opt/Cargo.toml
  3. 44 10
      packages/cli-opt/src/css.rs
  4. 9 1
      packages/cli/src/serve/handle.rs

+ 24 - 0
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"

+ 1 - 1
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"

+ 44 - 10
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<String> {
+    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(),

+ 9 - 1
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}");