Explorar el Código

Merge https://github.com/DioxusLabs/cli into hot_reload

Evan Almloff hace 3 años
padre
commit
11d045deeb
Se han modificado 11 ficheros con 361 adiciones y 18 borrados
  1. 5 1
      Cargo.toml
  2. 2 2
      rustfmt.toml
  3. 16 1
      src/assets/dioxus.toml
  4. 194 0
      src/builder.rs
  5. 5 0
      src/cli/build/mod.rs
  6. 26 0
      src/cli/cfg.rs
  7. 7 3
      src/cli/serve/mod.rs
  8. 7 4
      src/cli/tool/mod.rs
  9. 24 0
      src/config.rs
  10. 2 2
      src/server/mod.rs
  11. 73 5
      src/tools.rs

+ 5 - 1
Cargo.toml

@@ -2,7 +2,7 @@
 name = "dioxus-cli"
 version = "0.1.4"
 authors = ["Jonathan Kelley"]
-edition = "2018"
+edition = "2021"
 description = "CLI tool for developing, testing, and publishing Dioxus apps"
 license = "MIT/Apache-2.0"
 
@@ -39,11 +39,15 @@ hyper = "0.14.17"
 axum = { version = "0.5.1", features = ["ws", "headers"] }
 tower-http = { version = "0.2.2", features = ["fs", "trace"] }
 headers = "0.3.7"
+
+walkdir = "2"
+
 # tools download
 dirs = "4.0.0"
 reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] }
 flate2 = "1.0.22"
 tar = "0.4.38"
+zip = "0.6.2"
 tower = "0.4.12"
 
 syn = { version = "1.0" }

+ 2 - 2
rustfmt.toml

@@ -1,8 +1,8 @@
 version = "Two"
-edition = "2018"
+edition = "2021"
 
 imports_granularity = "Crate"
 #use_small_heuristics = "Max"
 #control_brace_style = "ClosingNextLine"
 normalize_comments = true
-format_code_in_doc_comments = true
+format_code_in_doc_comments = true

+ 16 - 1
src/assets/dioxus.toml

@@ -42,4 +42,19 @@ script = []
 
 # use binaryen.wasm-opt for output Wasm file
 # binaryen just will trigger in `web` platform
-binaryen = { wasm_opt = true }
+binaryen = { wasm_opt = true }
+
+# use sass auto will auto check sass file and build it.
+[application.tools.sass]
+
+# auto will check the assets dirs, and auto to transform all scss file to css file.
+input = "*"
+
+# or you can specify some scss file -> css file
+# input = [
+#     # some sass file path
+#     # this file will translate to `/css/test.css`
+#     "/css/test.scss"
+# ]
+
+source_map = true

+ 194 - 0
src/builder.rs

@@ -1,6 +1,7 @@
 use crate::{
     config::{CrateConfig, ExecutableType},
     error::{Error, Result},
+    tools::Tool,
     DioxusConfig,
 };
 use std::{
@@ -29,6 +30,9 @@ pub fn build(config: &CrateConfig) -> Result<()> {
         ..
     } = config;
 
+    // start to build the assets
+    let ignore_files = build_assets(config)?;
+
     let t_start = std::time::Instant::now();
 
     // [1] Build the .wasm module
@@ -44,6 +48,21 @@ pub fn build(config: &CrateConfig) -> Result<()> {
     if config.release {
         cmd.arg("--release");
     }
+    if config.verbose {
+        cmd.arg("--verbose");
+    }
+
+    if config.custom_profile.is_some() {
+        let custom_profile = config.custom_profile.as_ref().unwrap();
+        cmd.arg("--profile");
+        cmd.arg(custom_profile);
+    }
+
+    if config.features.is_some() {
+        let features_str = config.features.as_ref().unwrap().join(" ");
+        cmd.arg("--features");
+        cmd.arg(features_str);
+    }
 
     match executable {
         ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
@@ -154,6 +173,13 @@ pub fn build(config: &CrateConfig) -> Result<()> {
                         log::warn!("Error copying dir: {}", _e);
                     }
                 }
+                for ignore in &ignore_files {
+                    let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
+                    let ignore = config.out_dir.join(ignore);
+                    if ignore.is_file() {
+                        std::fs::remove_file(ignore)?;
+                    }
+                }
             }
         }
     }
@@ -166,6 +192,8 @@ pub fn build(config: &CrateConfig) -> Result<()> {
 pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
     log::info!("🚅 Running build [Desktop] command...");
 
+    let ignore_files = build_assets(config)?;
+
     let mut cmd = Command::new("cargo");
     cmd.current_dir(&config.crate_dir)
         .arg("build")
@@ -175,6 +203,21 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
     if config.release {
         cmd.arg("--release");
     }
+    if config.verbose {
+        cmd.arg("--verbose");
+    }
+
+    if config.custom_profile.is_some() {
+        let custom_profile = config.custom_profile.as_ref().unwrap();
+        cmd.arg("--profile");
+        cmd.arg(custom_profile);
+    }
+
+    if config.features.is_some() {
+        let features_str = config.features.as_ref().unwrap().join(" ");
+        cmd.arg("--features");
+        cmd.arg(features_str);
+    }
 
     match &config.executable {
         crate::ExecutableType::Binary(name) => cmd.arg("--bin").arg(name),
@@ -250,6 +293,13 @@ pub fn build_desktop(config: &CrateConfig, is_serve: bool) -> Result<()> {
                             log::warn!("Error copying dir: {}", e);
                         }
                     }
+                    for ignore in &ignore_files {
+                        let ignore = ignore.strip_prefix(&config.asset_dir).unwrap();
+                        let ignore = config.out_dir.join(ignore);
+                        if ignore.is_file() {
+                            std::fs::remove_file(ignore)?;
+                        }
+                    }
                 }
             }
         }
@@ -339,6 +389,150 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
     html.replace("{app_title}", &title)
 }
 
+// this function will build some assets file
+// like sass tool resources
+// this function will return a array which file don't need copy to out_dir.
+fn build_assets(config: &CrateConfig) -> Result<Vec<PathBuf>> {
+    let mut result = vec![];
+
+    let dioxus_config = &config.dioxus_config;
+    let dioxus_tools = dioxus_config.application.tools.clone().unwrap_or_default();
+
+    // check sass tool state
+    let sass = Tool::Sass;
+    if sass.is_installed() && dioxus_tools.contains_key("sass") {
+        let sass_conf = dioxus_tools.get("sass").unwrap();
+        if let Some(tab) = sass_conf.as_table() {
+            let source_map = tab.contains_key("source_map");
+            let source_map = if source_map && tab.get("source_map").unwrap().is_bool() {
+                if tab.get("source_map").unwrap().as_bool().unwrap_or_default() {
+                    "--source-map"
+                } else {
+                    "--no-source-map"
+                }
+            } else {
+                "--source-map"
+            };
+
+            if tab.contains_key("input") {
+                if tab.get("input").unwrap().is_str() {
+                    let file = tab.get("input").unwrap().as_str().unwrap().trim();
+
+                    if file == "*" {
+                        // if the sass open auto, we need auto-check the assets dir.
+                        let asset_dir = config.asset_dir.clone();
+                        if asset_dir.is_dir() {
+                            for entry in walkdir::WalkDir::new(&asset_dir)
+                                .into_iter()
+                                .filter_map(|e| e.ok())
+                            {
+                                let temp = entry.path();
+                                if temp.is_file() {
+                                    let suffix = temp.extension();
+                                    if suffix.is_none() { continue; }
+                                    let suffix = suffix.unwrap().to_str().unwrap();
+                                    if suffix == "scss" || suffix == "sass" {
+                                        // if file suffix is `scss` / `sass` we need transform it.
+                                        let out_file = format!(
+                                            "{}.css",
+                                            temp.file_stem().unwrap().to_str().unwrap()
+                                        );
+                                        let target_path = config
+                                            .out_dir
+                                            .join(
+                                                temp.strip_prefix(&asset_dir)
+                                                    .unwrap()
+                                                    .parent()
+                                                    .unwrap(),
+                                            )
+                                            .join(out_file);
+                                        let res = sass.call(
+                                            "sass",
+                                            vec![
+                                                temp.to_str().unwrap(),
+                                                target_path.to_str().unwrap(),
+                                                source_map,
+                                            ],
+                                        );
+                                        if res.is_ok() {
+                                            result.push(temp.to_path_buf());
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } else {
+                        // just transform one file.
+                        let relative_path = if &file[0..1] == "/" {
+                            &file[1..file.len()]
+                        } else {
+                            file
+                        };
+                        let path = config.asset_dir.join(relative_path);
+                        let out_file =
+                            format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
+                        let target_path = config
+                            .out_dir
+                            .join(PathBuf::from(relative_path).parent().unwrap())
+                            .join(out_file);
+                        if path.is_file() {
+                            let res = sass.call(
+                                "sass",
+                                vec![
+                                    path.to_str().unwrap(),
+                                    target_path.to_str().unwrap(),
+                                    source_map,
+                                ],
+                            );
+                            if res.is_ok() {
+                                result.push(path);
+                            } else {
+                                log::error!("{:?}", res);
+                            }
+                        }
+                    }
+                } else if tab.get("input").unwrap().is_array() {
+                    // check files list.
+                    let list = tab.get("input").unwrap().as_array().unwrap();
+                    for i in list {
+                        if i.is_str() {
+                            let path = i.as_str().unwrap();
+                            let relative_path = if &path[0..1] == "/" {
+                                &path[1..path.len()]
+                            } else {
+                                path
+                            };
+                            let path = config.asset_dir.join(relative_path);
+                            let out_file =
+                                format!("{}.css", path.file_stem().unwrap().to_str().unwrap());
+                            let target_path = config
+                                .out_dir
+                                .join(PathBuf::from(relative_path).parent().unwrap())
+                                .join(out_file);
+                            if path.is_file() {
+                                let res = sass.call(
+                                    "sass",
+                                    vec![
+                                        path.to_str().unwrap(),
+                                        target_path.to_str().unwrap(),
+                                        source_map,
+                                    ],
+                                );
+                                if res.is_ok() {
+                                    result.push(path);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    // SASS END
+
+    Ok(result)
+}
+
 // use binary_install::{Cache, Download};
 
 // /// Attempts to find `wasm-opt` in `PATH` locally, or failing that downloads a

+ 5 - 0
src/cli/build/mod.rs

@@ -14,11 +14,16 @@ impl Build {
 
         // change the release state.
         crate_config.with_release(self.build.release);
+        crate_config.with_verbose(self.build.verbose);
 
         if self.build.example.is_some() {
             crate_config.as_example(self.build.example.unwrap());
         }
 
+        if self.build.profile.is_some() {
+            crate_config.set_profile(self.build.profile.unwrap());
+        }
+
         let platform = self.build.platform.unwrap_or_else(|| {
             crate_config
                 .dioxus_config

+ 26 - 0
src/cli/cfg.rs

@@ -12,13 +12,26 @@ pub struct ConfigOptsBuild {
     #[serde(default)]
     pub release: bool,
 
+    // Use verbose output [default: false]
+    #[clap(long)]
+    #[serde(default)]
+    pub verbose: bool,
+
     /// Build a example [default: ""]
     #[clap(long)]
     pub example: Option<String>,
 
+    /// Build with custom profile
+    #[clap(long)]
+    pub profile: Option<String>,
+
     /// Build platform: support Web & Desktop [default: "default_platform"]
     #[clap(long)]
     pub platform: Option<String>,
+
+    /// Space separated list of features to activate
+    #[clap(long)]
+    pub features: Option<Vec<String>>,
 }
 
 #[derive(Clone, Debug, Default, Deserialize, Parser)]
@@ -36,6 +49,15 @@ pub struct ConfigOptsServe {
     #[serde(default)]
     pub release: bool,
 
+    // Use verbose output [default: false]
+    #[clap(long)]
+    #[serde(default)]
+    pub verbose: bool,
+
+    /// Build with custom profile
+    #[clap(long)]
+    pub profile: Option<String>,
+
     /// Build platform: support Web & Desktop [default: "default_platform"]
     #[clap(long)]
     pub platform: Option<String>,
@@ -44,6 +66,10 @@ pub struct ConfigOptsServe {
     #[clap(long)]
     #[serde(default)]
     pub hot_reload: bool,
+
+    /// Space separated list of features to activate
+    #[clap(long)]
+    pub features: Option<Vec<String>>,
 }
 
 /// Ensure the given value for `--public-url` is formatted correctly.

+ 7 - 3
src/cli/serve/mod.rs

@@ -18,14 +18,18 @@ impl Serve {
         let mut crate_config = crate::CrateConfig::new()?;
 
         // change the relase state.
-        crate_config
-            .with_release(self.serve.release)
-            .with_hot_reload(self.serve.hot_reload);
+        crate_config.with_hot_reload(self.serve.hot_reload);
+        crate_config.with_release(self.serve.release);
+        crate_config.with_verbose(self.serve.verbose);
 
         if self.serve.example.is_some() {
             crate_config.as_example(self.serve.example.unwrap());
         }
 
+        if self.serve.profile.is_some() {
+            crate_config.set_profile(self.serve.profile.unwrap());
+        }
+
         let platform = self.serve.platform.unwrap_or_else(|| {
             crate_config
                 .dioxus_config

+ 7 - 4
src/cli/tool/mod.rs

@@ -20,14 +20,18 @@ impl Tool {
             Tool::List {} => {
                 for item in tools::tool_list() {
                     if tools::Tool::from_str(item).unwrap().is_installed() {
-                        println!("{item} [installed]");
+                        println!("- {item} [installed]");
                     } else {
-                        println!("{item}");
+                        println!("- {item}");
                     }
                 }
             }
             Tool::AppPath {} => {
-                println!("{}", tools::tools_path().to_str().unwrap());
+                if let Some(v) = tools::tools_path().to_str() {
+                    println!("{}", v);
+                } else {
+                    log::error!("Tools path get failed.");
+                }
             }
             Tool::Add { name } => {
                 let tool_list = tools::tool_list();
@@ -58,7 +62,6 @@ impl Tool {
                 log::info!("Tool {name} install successfully!");
             }
         }
-
         Ok(())
     }
 }

+ 24 - 0
src/config.rs

@@ -115,6 +115,9 @@ pub struct CrateConfig {
     pub dioxus_config: DioxusConfig,
     pub release: bool,
     pub hot_reload: bool,
+    pub verbose: bool,
+    pub custom_profile: Option<String>,
+    pub features: Option<Vec<String>>,
 }
 
 #[derive(Debug, Clone)]
@@ -164,6 +167,9 @@ impl CrateConfig {
 
         let release = false;
         let hot_reload = false;
+        let verbose = false;
+        let custom_profile = None;
+        let features = None;
 
         Ok(Self {
             out_dir,
@@ -176,6 +182,9 @@ impl CrateConfig {
             release,
             dioxus_config,
             hot_reload,
+            custom_profile,
+            features,
+            verbose,
         })
     }
 
@@ -194,6 +203,21 @@ impl CrateConfig {
         self
     }
 
+    pub fn with_verbose(&mut self, verbose: bool) -> &mut Self {
+        self.verbose = verbose;
+        self
+    }
+
+    pub fn set_profile(&mut self, profile: String) -> &mut Self {
+        self.custom_profile = Some(profile);
+        self
+    }
+
+    pub fn set_features(&mut self, features: Vec<String>) -> &mut Self {
+        self.features = Some(features);
+        self
+    }
+
     // pub fn with_build_options(&mut self, options: &BuildOptions) {
     //     if let Some(name) = &options.example {
     //         self.as_example(name.clone());

+ 2 - 2
src/server/mod.rs

@@ -189,7 +189,7 @@ pub async fn startup_hot_reload(config: CrateConfig) -> Result<()> {
     let file_service_config = config.clone();
     let file_service = ServiceBuilder::new()
         .and_then(
-            |response: Response<ServeFileSystemResponseBody>| async move {
+            move |response: Response<ServeFileSystemResponseBody>| async move {
                 let response = if file_service_config
                     .dioxus_config
                     .web
@@ -300,7 +300,7 @@ pub async fn startup_default(config: CrateConfig) -> Result<()> {
     let file_service_config = config.clone();
     let file_service = ServiceBuilder::new()
         .and_then(
-            |response: Response<ServeFileSystemResponseBody>| async move {
+            move |response: Response<ServeFileSystemResponseBody>| async move {
                 let response = if file_service_config
                     .dioxus_config
                     .web

+ 73 - 5
src/tools.rs

@@ -1,6 +1,6 @@
 use std::{
     fs::{create_dir_all, File},
-    path::PathBuf,
+    path::{Path, PathBuf},
     process::Command,
 };
 
@@ -13,10 +13,11 @@ use tokio::io::AsyncWriteExt;
 #[derive(Debug, PartialEq, Eq)]
 pub enum Tool {
     Binaryen,
+    Sass,
 }
 
 pub fn tool_list() -> Vec<&'static str> {
-    vec!["binaryen"]
+    vec!["binaryen", "sass"]
 }
 
 pub fn app_path() -> PathBuf {
@@ -52,6 +53,7 @@ impl Tool {
     pub fn from_str(name: &str) -> Option<Self> {
         match name {
             "binaryen" => Some(Self::Binaryen),
+            "sass" => Some(Self::Sass),
             _ => None,
         }
     }
@@ -60,6 +62,7 @@ impl Tool {
     pub fn name(&self) -> &str {
         match self {
             Self::Binaryen => "binaryen",
+            Self::Sass => "sass",
         }
     }
 
@@ -67,6 +70,7 @@ impl Tool {
     pub fn bin_path(&self) -> &str {
         match self {
             Self::Binaryen => "bin",
+            Self::Sass => ".",
         }
     }
 
@@ -84,6 +88,17 @@ impl Tool {
                     panic!("unsupported platformm");
                 }
             }
+            Self::Sass => {
+                if cfg!(target_os = "windows") {
+                    "windows"
+                } else if cfg!(target_os = "macos") {
+                    "macos"
+                } else if cfg!(target_os = "linux") {
+                    "linux"
+                } else {
+                    panic!("unsupported platformm");
+                }
+            }
         }
     }
 
@@ -96,6 +111,13 @@ impl Tool {
                     target = self.target_platform()
                 )
             }
+            Self::Sass => {
+                format!(
+                    "https://github.com/sass/dart-sass/releases/download/1.51.0/dart-sass-1.51.0-{target}-x64.{extension}",
+                    target = self.target_platform(),
+                    extension = self.extension()
+                )
+            }
         }
     }
 
@@ -103,6 +125,13 @@ impl Tool {
     pub fn extension(&self) -> &str {
         match self {
             Self::Binaryen => "tar.gz",
+            Self::Sass => {
+                if cfg!(target_os = "windows") {
+                    "zip"
+                } else {
+                    "tar.gz"
+                }
+            }
         }
     }
 
@@ -131,7 +160,7 @@ impl Tool {
             let chunk = chunk_res.context("error reading chunk from download")?;
             let _ = file.write(chunk.as_ref()).await;
         }
-
+        // log::info!("temp file path: {:?}", temp_out);
         Ok(temp_out)
     }
 
@@ -143,7 +172,7 @@ impl Tool {
         let dir_name = if self == &Tool::Binaryen {
             "binaryen-version_105"
         } else {
-            ""
+            "dart-sass"
         };
 
         if self.extension() == "tar.gz" {
@@ -151,7 +180,10 @@ impl Tool {
             let tar = GzDecoder::new(tar_gz);
             let mut archive = Archive::new(tar);
             archive.unpack(&tool_path)?;
-            // println!("{:?} -> {:?}", tool_path.join(dir_name), tool_path.join(self.name()));
+            std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
+        } else if self.extension() == "zip" {
+            // decompress the `zip` file
+            extract_zip(&temp_path, &tool_path)?;
             std::fs::rename(tool_path.join(dir_name), tool_path.join(self.name()))?;
         }
 
@@ -169,6 +201,13 @@ impl Tool {
                     command.to_string()
                 }
             }
+            Tool::Sass => {
+                if cfg!(target_os = "windows") {
+                    format!("{}.bat", command)
+                } else {
+                    command.to_string()
+                }
+            }
         };
 
         if !bin_path.join(&command_file).is_file() {
@@ -185,3 +224,32 @@ impl Tool {
         Ok(output.stdout)
     }
 }
+
+fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
+    let zip_file = std::fs::File::open(&file)?;
+    let mut zip = zip::ZipArchive::new(zip_file)?;
+
+    if !target.exists() {
+        let _ = std::fs::create_dir_all(target)?;
+    }
+
+    for i in 0..zip.len() {
+        let mut file = zip.by_index(i)?;
+        if file.is_dir() {
+            // dir
+            let target = target.join(Path::new(&file.name().replace('\\', "")));
+            let _ = std::fs::create_dir_all(target)?;
+        } else {
+            // file
+            let file_path = target.join(Path::new(file.name()));
+            let mut target_file = if !file_path.exists() {
+                std::fs::File::create(file_path)?
+            } else {
+                std::fs::File::open(file_path)?
+            };
+            let _num = std::io::copy(&mut file, &mut target_file)?;
+        }
+    }
+
+    Ok(())
+}