Forráskód Böngészése

Merge branch 'master' into breaking

Evan Almloff 1 éve
szülő
commit
d44b0b34c8

+ 1 - 1
.github/workflows/wipe_cache.yml

@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Clear cache
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
           github-token: ${{ secrets.cache_controller }}
           script: |

+ 40 - 35
packages/cli/src/cli/create.rs

@@ -21,47 +21,52 @@ impl Create {
 
         let path = cargo_generate::generate(args)?;
 
-        // first run cargo fmt
-        let mut cmd = Command::new("cargo");
-        let cmd = cmd.arg("fmt").current_dir(&path);
-        let output = cmd.output().expect("failed to execute process");
-        if !output.status.success() {
-            log::error!("cargo fmt failed");
-            log::error!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-            log::error!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-        }
+        post_create(&path)
+    }
+}
+
+// being also used by `init`
+pub fn post_create(path: &PathBuf) -> Result<()> {
+    // first run cargo fmt
+    let mut cmd = Command::new("cargo");
+    let cmd = cmd.arg("fmt").current_dir(path);
+    let output = cmd.output().expect("failed to execute process");
+    if !output.status.success() {
+        log::error!("cargo fmt failed");
+        log::error!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+        log::error!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+    }
 
-        // then format the toml
-        let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")];
-        for toml_path in &toml_paths {
-            let toml = std::fs::read_to_string(toml_path)?;
-            let mut toml = toml.parse::<toml_edit::Document>().map_err(|e| {
-                anyhow::anyhow!(
-                    "failed to parse toml at {}: {}",
-                    toml_path.display(),
-                    e.to_string()
-                )
-            })?;
+    // then format the toml
+    let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")];
+    for toml_path in &toml_paths {
+        let toml = std::fs::read_to_string(toml_path)?;
+        let mut toml = toml.parse::<toml_edit::Document>().map_err(|e| {
+            anyhow::anyhow!(
+                "failed to parse toml at {}: {}",
+                toml_path.display(),
+                e.to_string()
+            )
+        })?;
 
-            toml.as_table_mut().fmt();
+        toml.as_table_mut().fmt();
 
-            let as_string = toml.to_string();
-            let new_string = remove_triple_newlines(&as_string);
-            let mut file = std::fs::File::create(toml_path)?;
-            file.write_all(new_string.as_bytes())?;
-        }
+        let as_string = toml.to_string();
+        let new_string = remove_triple_newlines(&as_string);
+        let mut file = std::fs::File::create(toml_path)?;
+        file.write_all(new_string.as_bytes())?;
+    }
 
-        // remove any triple newlines from the readme
-        let readme_path = path.join("README.md");
-        let readme = std::fs::read_to_string(&readme_path)?;
-        let new_readme = remove_triple_newlines(&readme);
-        let mut file = std::fs::File::create(readme_path)?;
-        file.write_all(new_readme.as_bytes())?;
+    // remove any triple newlines from the readme
+    let readme_path = path.join("README.md");
+    let readme = std::fs::read_to_string(&readme_path)?;
+    let new_readme = remove_triple_newlines(&readme);
+    let mut file = std::fs::File::create(readme_path)?;
+    file.write_all(new_readme.as_bytes())?;
 
-        log::info!("Generated project at {}", path.display());
+    log::info!("Generated project at {}", path.display());
 
-        Ok(())
-    }
+    Ok(())
 }
 
 fn remove_triple_newlines(string: &str) -> String {

+ 33 - 0
packages/cli/src/cli/init.rs

@@ -0,0 +1,33 @@
+use super::*;
+use cargo_generate::{GenerateArgs, TemplatePath};
+
+#[derive(Clone, Debug, Default, Deserialize, Parser)]
+#[clap(name = "init")]
+pub struct Init {
+    /// Template path
+    #[clap(default_value = "gh:dioxuslabs/dioxus-template", long)]
+    template: String,
+}
+
+impl Init {
+    pub fn init(self) -> Result<()> {
+        // get dir name
+        let name = std::env::current_dir()?
+            .file_name()
+            .map(|f| f.to_str().unwrap().to_string());
+
+        let args = GenerateArgs {
+            template_path: TemplatePath {
+                auto_path: Some(self.template),
+                ..Default::default()
+            },
+            name,
+            init: true,
+            ..Default::default()
+        };
+
+        let path = cargo_generate::generate(args)?;
+
+        create::post_create(&path)
+    }
+}

+ 6 - 1
packages/cli/src/cli/mod.rs

@@ -6,6 +6,7 @@ pub mod check;
 pub mod clean;
 pub mod config;
 pub mod create;
+pub mod init;
 pub mod plugin;
 pub mod serve;
 pub mod translate;
@@ -56,9 +57,12 @@ pub enum Commands {
     /// Build, watch & serve the Rust WASM app and all of its assets.
     Serve(serve::Serve),
 
-    /// Init a new project for Dioxus.
+    /// Create a new project for Dioxus.
     Create(create::Create),
 
+    /// Init a new project for Dioxus
+    Init(init::Init),
+
     /// Clean output artifacts.
     Clean(clean::Clean),
 
@@ -94,6 +98,7 @@ impl Display for Commands {
             Commands::Translate(_) => write!(f, "translate"),
             Commands::Serve(_) => write!(f, "serve"),
             Commands::Create(_) => write!(f, "create"),
+            Commands::Init(_) => write!(f, "init"),
             Commands::Clean(_) => write!(f, "clean"),
             Commands::Config(_) => write!(f, "config"),
             Commands::Version(_) => write!(f, "version"),

+ 6 - 6
packages/cli/src/cli/serve.rs

@@ -23,16 +23,16 @@ impl Serve {
         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 let Some(example) = self.serve.example {
+            crate_config.as_example(example);
         }
 
-        if self.serve.profile.is_some() {
-            crate_config.set_profile(self.serve.profile.unwrap());
+        if let Some(profile) = self.serve.profile {
+            crate_config.set_profile(profile);
         }
 
-        if self.serve.features.is_some() {
-            crate_config.set_features(self.serve.features.unwrap());
+        if let Some(features) = self.serve.features {
+            crate_config.set_features(features);
         }
 
         if let Some(target) = self.serve.target {

+ 0 - 1
packages/cli/src/logging.rs

@@ -25,7 +25,6 @@ pub fn set_up_logging() {
                     colors_line.get_color(&record.level()).to_fg_str()
                 ),
                 level = colors_level.color(record.level()),
-                message = message,
             ));
         })
         .level(match std::env::var("DIOXUS_LOG") {

+ 38 - 33
packages/cli/src/main.rs

@@ -43,51 +43,23 @@ async fn main() -> anyhow::Result<()> {
 
     set_up_logging();
 
-    let bin = get_bin(args.bin);
-
-    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::info!("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}"))?;
-    }
-
     match args.action {
         Translate(opts) => opts
             .translate()
             .map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)),
 
-        Build(opts) if bin.is_ok() => opts
-            .build(Some(bin.unwrap().clone()), None)
-            .map_err(|e| anyhow!("🚫 Building project failed: {}", e)),
-
-        Clean(opts) if bin.is_ok() => opts
-            .clean(Some(bin.unwrap().clone()))
-            .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)),
-
-        Serve(opts) if bin.is_ok() => opts
-            .serve(Some(bin.unwrap().clone()))
-            .await
-            .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)),
-
         Create(opts) => opts
             .create()
             .map_err(|e| anyhow!("🚫 Creating new project failed: {}", e)),
 
+        Init(opts) => opts
+            .init()
+            .map_err(|e| anyhow!("🚫 Initialising a new project failed: {}", e)),
+
         Config(opts) => opts
             .config()
             .map_err(|e| anyhow!("🚫 Configuring new project failed: {}", e)),
 
-        Bundle(opts) if bin.is_ok() => opts
-            .bundle(Some(bin.unwrap().clone()))
-            .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)),
-
         #[cfg(feature = "plugin")]
         Plugin(opts) => opts
             .plugin()
@@ -110,6 +82,39 @@ async fn main() -> anyhow::Result<()> {
 
             Ok(())
         }
-        _ => Err(anyhow::anyhow!(bin.unwrap_err())),
+        action => {
+            let bin = get_bin(args.bin)?;
+            let _dioxus_config = DioxusConfig::load(Some(bin.clone()))
+                       .map_err(|e| anyhow!("Failed to load Dioxus config because: {e}"))?
+                       .unwrap_or_else(|| {
+                           log::info!("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}"))?;
+
+            match action {
+                Build(opts) => opts
+                    .build(Some(bin.clone()), None)
+                    .map_err(|e| anyhow!("🚫 Building project failed: {}", e)),
+
+                Clean(opts) => opts
+                    .clean(Some(bin.clone()))
+                    .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)),
+
+                Serve(opts) => opts
+                    .serve(Some(bin.clone()))
+                    .await
+                    .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)),
+
+                Bundle(opts) => opts
+                    .bundle(Some(bin.clone()))
+                    .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)),
+
+                _ => unreachable!(),
+            }
+        }
     }
 }

+ 32 - 0
packages/html/src/events/form.rs

@@ -1,5 +1,6 @@
 use crate::file_data::FileEngine;
 use crate::file_data::HasFileData;
+use std::ops::Deref;
 use std::{collections::HashMap, fmt::Debug};
 
 use dioxus_core::Event;
@@ -19,6 +20,37 @@ pub enum FormValue {
     VecText(Vec<String>),
 }
 
+impl From<FormValue> for Vec<String> {
+    fn from(value: FormValue) -> Self {
+        match value {
+            FormValue::Text(s) => vec![s],
+            FormValue::VecText(vec) => vec,
+        }
+    }
+}
+
+impl Deref for FormValue {
+    type Target = [String];
+
+    fn deref(&self) -> &Self::Target {
+        self.as_slice()
+    }
+}
+
+impl FormValue {
+    /// Convenient way to represent Value as slice
+    pub fn as_slice(&self) -> &[String] {
+        match self {
+            FormValue::Text(s) => std::slice::from_ref(s),
+            FormValue::VecText(vec) => vec.as_slice(),
+        }
+    }
+    /// Convert into Vec<String>
+    pub fn to_vec(self) -> Vec<String> {
+        self.into()
+    }
+}
+
 /* DOMEvent:  Send + SyncTarget relatedTarget */
 pub struct FormData {
     inner: Box<dyn HasFormData>,

+ 27 - 23
packages/web/src/event.rs

@@ -389,7 +389,25 @@ impl HasFormData for WebFormData {
     }
 
     fn values(&self) -> HashMap<String, FormValue> {
-        let mut values = std::collections::HashMap::new();
+        let mut values = HashMap::new();
+
+        fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
+            match map.entry(key) {
+                std::collections::hash_map::Entry::Occupied(mut o) => {
+                    let first_value = match o.get_mut() {
+                        FormValue::Text(data) => std::mem::take(data),
+                        FormValue::VecText(vec) => {
+                            vec.push(new_value);
+                            return;
+                        }
+                    };
+                    let _ = o.insert(FormValue::VecText(vec![first_value, new_value]));
+                }
+                std::collections::hash_map::Entry::Vacant(v) => {
+                    let _ = v.insert(FormValue::Text(new_value));
+                }
+            }
+        }
 
         // try to fill in form values
         if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
@@ -398,20 +416,18 @@ impl HasFormData for WebFormData {
                 if let Ok(array) = value.dyn_into::<Array>() {
                     if let Some(name) = array.get(0).as_string() {
                         if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
-                            let item_values: Vec<String> =
-                                item_values.iter().filter_map(|v| v.as_string()).collect();
-
-                            values.insert(name, FormValue::VecText(item_values));
+                            item_values
+                                .iter()
+                                .filter_map(|v| v.as_string())
+                                .for_each(|v| insert_value(&mut values, name.clone(), v));
                         } else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
-                            values.insert(name, FormValue::Text(item_value.as_string().unwrap()));
+                            insert_value(&mut values, name, item_value.as_string().unwrap());
                         }
                     }
                 }
             }
-        }
-
-        // try to fill in select element values
-        if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
+        } else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
+            // try to fill in select element values
             let options = get_select_data(select);
             values.insert("options".to_string(), FormValue::VecText(options));
         }
@@ -536,19 +552,7 @@ export function get_form_data(form) {
     const formData = new FormData(form);
 
     for (let name of formData.keys()) {
-        const fieldType = form.elements[name].type;
-        console.log(fieldType);
-
-        switch (fieldType) {
-            case "select-multiple":
-                values.set(name, formData.getAll(name));
-                break;
-
-            // add cases for fieldTypes that can hold multiple values here
-            default:
-                values.set(name, formData.get(name));
-                break;
-        }
+        values.set(name, formData.getAll(name));
     }
 
     return values;