Pārlūkot izejas kodu

Merge branch 'master' into pr/atty303/1349

Evan Almloff 1 gadu atpakaļ
vecāks
revīzija
355cd9b19e

+ 1 - 1
Cargo.toml

@@ -90,7 +90,7 @@ slab = "0.4.2"
 futures-channel = "0.3.21"
 futures-util = { version = "0.3", default-features = false }
 rustc-hash = "1.1.0"
-wasm-bindgen = "0.2.87"
+wasm-bindgen = "0.2.88"
 html_parser = "0.7.0"
 thiserror = "1.0.40"
 prettyplease = { package = "prettier-please", version = "0.2", features = [

+ 1 - 0
examples/calculator.rs

@@ -62,6 +62,7 @@ fn app(cx: Scope) -> Element {
         div { id: "wrapper",
             div { class: "app",
                 div { class: "calculator",
+                    tabindex: "0",
                     onkeydown: handle_key_down_event,
                     div { class: "calculator-display", val.to_string() }
                     div { class: "calculator-keypad",

+ 1 - 1
packages/cli/README.md

@@ -11,7 +11,7 @@ It handles building, bundling, development and publishing to simplify developmen
 ### Install the stable version (recommended)
 
 ```
-cargo install dioxus-cli --locked
+cargo install dioxus-cli
 ```
 
 ### Install the latest development build through git

+ 1 - 1
packages/cli/src/builder.rs

@@ -469,7 +469,7 @@ pub fn gen_page(config: &DioxusConfig, serve: bool) -> String {
         .unwrap_or_default()
         .contains_key("tailwindcss")
     {
-        style_str.push_str("<link rel=\"stylesheet\" href=\"tailwind.css\">\n");
+        style_str.push_str("<link rel=\"stylesheet\" href=\"/{base_path}/tailwind.css\">\n");
     }
 
     replace_or_insert_before("{style_include}", &style_str, "</head", &mut html);

+ 3 - 0
packages/cli/src/error.rs

@@ -29,6 +29,9 @@ pub enum Error {
     #[error("Cargo Error: {0}")]
     CargoError(String),
 
+    #[error("Couldn't retrieve cargo metadata")]
+    CargoMetadata(#[source] cargo_metadata::Error),
+
     #[error("{0}")]
     CustomError(String),
 

+ 30 - 41
packages/cli/src/main.rs

@@ -9,42 +9,31 @@ use dioxus_cli::plugin::PluginManager;
 
 use Commands::*;
 
-fn get_bin(bin: Option<String>) -> Result<Option<PathBuf>> {
-    const ERR_MESSAGE: &str = "The `--bin` flag has to be ran in a Cargo workspace.";
-
-    if let Some(ref bin) = bin {
-        let manifest = cargo_toml::Manifest::from_path("./Cargo.toml")
-            .map_err(|_| Error::CargoError(ERR_MESSAGE.to_string()))?;
-
-        if let Some(workspace) = manifest.workspace {
-            for item in workspace.members.iter() {
-                let path = PathBuf::from(item);
-
-                if !path.exists() {
-                    continue;
-                }
-
-                if !path.is_dir() {
-                    continue;
-                }
-
-                if path.ends_with(bin.clone()) {
-                    return Ok(Some(path));
-                }
-            }
-        } else {
-            return Err(Error::CargoError(ERR_MESSAGE.to_string()));
-        }
-    }
-
-    // If the bin exists but we couldn't find it
-    if bin.is_some() {
-        return Err(Error::CargoError(
-            "The specified bin does not exist.".to_string(),
-        ));
-    }
-
-    Ok(None)
+fn get_bin(bin: Option<String>) -> Result<PathBuf> {
+    let metadata = cargo_metadata::MetadataCommand::new()
+        .exec()
+        .map_err(Error::CargoMetadata)?;
+    let package = if let Some(bin) = bin {
+        metadata
+            .workspace_packages()
+            .into_iter()
+            .find(|p| p.name == bin)
+            .ok_or(format!("no such package: {}", bin))
+            .map_err(Error::CargoError)?
+    } else {
+        metadata
+            .root_package()
+            .ok_or("no root package?".into())
+            .map_err(Error::CargoError)?
+    };
+
+    let crate_dir = package
+        .manifest_path
+        .parent()
+        .ok_or("couldn't take parent dir".into())
+        .map_err(Error::CargoError)?;
+
+    Ok(crate_dir.into())
 }
 
 #[tokio::main]
@@ -55,7 +44,7 @@ async fn main() -> anyhow::Result<()> {
 
     let bin = get_bin(args.bin)?;
 
-    let _dioxus_config = DioxusConfig::load(bin.clone())
+    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");
@@ -72,15 +61,15 @@ async fn main() -> anyhow::Result<()> {
             .map_err(|e| anyhow!("🚫 Translation of HTML into RSX failed: {}", e)),
 
         Build(opts) => opts
-            .build(bin.clone())
+            .build(Some(bin.clone()))
             .map_err(|e| anyhow!("🚫 Building project failed: {}", e)),
 
         Clean(opts) => opts
-            .clean(bin.clone())
+            .clean(Some(bin.clone()))
             .map_err(|e| anyhow!("🚫 Cleaning project failed: {}", e)),
 
         Serve(opts) => opts
-            .serve(bin.clone())
+            .serve(Some(bin.clone()))
             .await
             .map_err(|e| anyhow!("🚫 Serving project failed: {}", e)),
 
@@ -93,7 +82,7 @@ async fn main() -> anyhow::Result<()> {
             .map_err(|e| anyhow!("🚫 Configuring new project failed: {}", e)),
 
         Bundle(opts) => opts
-            .bundle(bin.clone())
+            .bundle(Some(bin.clone()))
             .map_err(|e| anyhow!("🚫 Bundling project failed: {}", e)),
 
         #[cfg(feature = "plugin")]

+ 1 - 0
packages/core-macro/src/component_body_deserializers/component.rs

@@ -31,6 +31,7 @@ fn get_out_comp_fn(orig_comp_fn: &ItemFn, cx_pat: &Pat) -> ItemFn {
         block: parse_quote! {
             {
                 #[warn(non_snake_case)]
+                #[allow(clippy::inline_always)]
                 #[inline(always)]
                 #inner_comp_fn
                 #inner_comp_ident (#cx_pat)

+ 7 - 7
packages/core-macro/src/props/mod.rs

@@ -632,18 +632,16 @@ mod struct_info {
             let generics_with_empty = modify_types_generics_hack(&b_initial_generics, |args| {
                 args.insert(0, syn::GenericArgument::Type(empties_tuple.clone().into()));
             });
-            let phantom_generics = self.generics.params.iter().map(|param| match param {
+            let phantom_generics = self.generics.params.iter().filter_map(|param| match param {
                 syn::GenericParam::Lifetime(lifetime) => {
                     let lifetime = &lifetime.lifetime;
-                    quote!(::core::marker::PhantomData<&#lifetime ()>)
+                    Some(quote!(::core::marker::PhantomData<&#lifetime ()>))
                 }
                 syn::GenericParam::Type(ty) => {
                     let ty = &ty.ident;
-                    quote!(::core::marker::PhantomData<#ty>)
-                }
-                syn::GenericParam::Const(_cnst) => {
-                    quote!()
+                    Some(quote!(::core::marker::PhantomData<#ty>))
                 }
+                syn::GenericParam::Const(_cnst) => None,
             });
             let builder_method_doc = match self.builder_attr.builder_method_doc {
                 Some(ref doc) => quote!(#doc),
@@ -738,7 +736,7 @@ Finally, call `.build()` to create the instance of `{name}`.
             Ok(quote! {
                 impl #impl_generics #name #ty_generics #where_clause {
                     #[doc = #builder_method_doc]
-                    #[allow(dead_code)]
+                    #[allow(dead_code, clippy::type_complexity)]
                     #vis fn builder(_cx: & #extend_lifetime ::dioxus::prelude::ScopeState) -> #builder_name #generics_with_empty {
                         #builder_name {
                             #(#extend_fields_value,)*
@@ -1073,6 +1071,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                 #[allow(dead_code, non_camel_case_types, missing_docs)]
                 impl #impl_generics #builder_name < #( #ty_generics ),* > #where_clause {
                     #doc
+                    #[allow(clippy::type_complexity)]
                     pub fn #field_name (self, #field_name: #arg_type) -> #builder_name < #( #target_generics ),* > {
                         let #field_name = (#arg_expr,);
                         let ( #(#descructuring,)* ) = self.fields;
@@ -1093,6 +1092,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                     #[deprecated(
                         note = #repeated_fields_error_message
                     )]
+                    #[allow(clippy::type_complexity)]
                     pub fn #field_name (self, _: #repeated_fields_error_type_name) -> #builder_name < #( #target_generics ),* > {
                         self
                     }

+ 3 - 9
packages/core/src/arena.rs

@@ -164,17 +164,11 @@ impl VirtualDom {
         });
 
         // Now that all the references are gone, we can safely drop our own references in our listeners.
-        let mut listeners = scope.attributes_to_drop.borrow_mut();
+        let mut listeners = scope.attributes_to_drop_before_render.borrow_mut();
         listeners.drain(..).for_each(|listener| {
             let listener = unsafe { &*listener };
-            match &listener.value {
-                AttributeValue::Listener(l) => {
-                    _ = l.take();
-                }
-                AttributeValue::Any(a) => {
-                    _ = a.take();
-                }
-                _ => (),
+            if let AttributeValue::Listener(l) = &listener.value {
+                _ = l.take();
             }
         });
     }

+ 22 - 3
packages/core/src/bump_frame.rs

@@ -1,10 +1,13 @@
 use crate::nodes::RenderReturn;
+use crate::{Attribute, AttributeValue};
 use bumpalo::Bump;
+use std::cell::RefCell;
 use std::cell::{Cell, UnsafeCell};
 
 pub(crate) struct BumpFrame {
     pub bump: UnsafeCell<Bump>,
     pub node: Cell<*const RenderReturn<'static>>,
+    pub(crate) attributes_to_drop_before_reset: RefCell<Vec<*const Attribute<'static>>>,
 }
 
 impl BumpFrame {
@@ -13,6 +16,7 @@ impl BumpFrame {
         Self {
             bump: UnsafeCell::new(bump),
             node: Cell::new(std::ptr::null()),
+            attributes_to_drop_before_reset: Default::default(),
         }
     }
 
@@ -31,8 +35,23 @@ impl BumpFrame {
         unsafe { &*self.bump.get() }
     }
 
-    #[allow(clippy::mut_from_ref)]
-    pub(crate) unsafe fn bump_mut(&self) -> &mut Bump {
-        unsafe { &mut *self.bump.get() }
+    pub(crate) fn add_attribute_to_drop(&self, attribute: *const Attribute<'static>) {
+        self.attributes_to_drop_before_reset
+            .borrow_mut()
+            .push(attribute);
+    }
+
+    pub(crate) unsafe fn reset(&self) {
+        let mut attributes = self.attributes_to_drop_before_reset.borrow_mut();
+        attributes.drain(..).for_each(|attribute| {
+            let attribute = unsafe { &*attribute };
+            if let AttributeValue::Any(l) = &attribute.value {
+                _ = l.take();
+            }
+        });
+        unsafe {
+            let bump = &mut *self.bump.get();
+            bump.reset();
+        }
     }
 }

+ 31 - 2
packages/core/src/lazynodes.rs

@@ -23,8 +23,37 @@ use crate::{innerlude::VNode, ScopeState};
 ///
 ///
 /// ```rust, ignore
-/// LazyNodes::new(|f| f.element("div", [], [], [] None))
+/// LazyNodes::new(|f| {
+///        static TEMPLATE: dioxus::core::Template = dioxus::core::Template {
+///         name: "main.rs:5:5:20", // Source location of the template for hot reloading
+///         roots: &[
+///             dioxus::core::TemplateNode::Element {
+///                 tag: dioxus_elements::div::TAG_NAME,
+///                 namespace: dioxus_elements::div::NAME_SPACE,
+///                 attrs: &[],
+///                 children: &[],
+///             },
+///         ],
+///         node_paths: &[],
+///         attr_paths: &[],
+///     };
+///     dioxus::core::VNode {
+///         parent: None,
+///         key: None,
+///         template: std::cell::Cell::new(TEMPLATE),
+///         root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(
+///                 1usize,
+///                 f.bump(),
+///             )
+///             .into(),
+///         dynamic_nodes: f.bump().alloc([]),
+///         dynamic_attrs: f.bump().alloc([]),
+///     })
+/// }
 /// ```
+///
+/// Find more information about how to construct [`VNode`] at <https://dioxuslabs.com/learn/0.4/contributing/walkthrough_readme#the-rsx-macro>
+
 pub struct LazyNodes<'a, 'b> {
     #[cfg(not(miri))]
     inner: SmallBox<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b, S16>,
@@ -61,7 +90,7 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
     /// Call the closure with the given factory to produce real [`VNode`].
     ///
     /// ```rust, ignore
-    /// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
+    /// let f = LazyNodes::new(/* Closure for creating VNodes */);
     ///
     /// let node = f.call(cac);
     /// ```

+ 2 - 2
packages/core/src/scope_arena.rs

@@ -35,7 +35,7 @@ impl VirtualDom {
             hook_idx: Default::default(),
 
             borrowed_props: Default::default(),
-            attributes_to_drop: Default::default(),
+            attributes_to_drop_before_render: Default::default(),
         }));
 
         let context =
@@ -54,7 +54,7 @@ impl VirtualDom {
 
         let new_nodes = unsafe {
             let scope = &self.scopes[scope_id.0];
-            scope.previous_frame().bump_mut().reset();
+            scope.previous_frame().reset();
 
             scope.context().suspended.set(false);
 

+ 16 - 6
packages/core/src/scopes.rs

@@ -95,7 +95,7 @@ pub struct ScopeState {
     pub(crate) hook_idx: Cell<usize>,
 
     pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
-    pub(crate) attributes_to_drop: RefCell<Vec<*const Attribute<'static>>>,
+    pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute<'static>>>,
 
     pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
 }
@@ -349,12 +349,22 @@ impl<'src> ScopeState {
     pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
         let element = rsx.call(self);
 
-        let mut listeners = self.attributes_to_drop.borrow_mut();
+        let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
         for attr in element.dynamic_attrs {
-            attr.ty.for_each(|attr| {
-                let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
-                listeners.push(unbounded);
-            })
+            match attr.value {
+                // We need to drop listeners before the next render because they may borrow data from the borrowed props which will be dropped
+                AttributeValue::Listener(_) => {
+                    let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
+                    listeners.push(unbounded);
+                }
+                // We need to drop any values manually to make sure that their drop implementation is called before the next render
+                AttributeValue::Any(_) => {
+                    let unbounded = unsafe { std::mem::transmute(attr as *const Attribute) };
+                    self.previous_frame().add_attribute_to_drop(unbounded);
+                }
+
+                _ => (),
+            }
         }
 
         let mut props = self.borrowed_props.borrow_mut();

+ 1 - 1
packages/fermi/src/hooks/atom_root.rs

@@ -7,6 +7,6 @@ use dioxus_core::ScopeState;
 pub fn use_atom_root(cx: &ScopeState) -> &Rc<AtomRoot> {
     cx.use_hook(|| match cx.consume_context::<Rc<AtomRoot>>() {
         Some(root) => root,
-        None => panic!("No atom root found in context. Did you forget place an AtomRoot component at the top of your app?"),
+        None => panic!("No atom root found in context. Did you forget to call use_init_atom_root at the top of your app?"),
     })
 }

+ 3 - 1
packages/fermi/src/hooks/state.rs

@@ -86,7 +86,9 @@ impl<T: 'static> AtomState<T> {
     /// ```
     #[must_use]
     pub fn current(&self) -> Rc<T> {
-        self.value.as_ref().unwrap().clone()
+        let atoms = self.root.atoms.borrow();
+        let slot = atoms.get(&self.id).unwrap();
+        slot.value.clone().downcast().unwrap()
     }
 
     /// Get the `setter` function directly without the `AtomState` wrapper.

+ 1 - 1
packages/fullstack/Cargo.toml

@@ -11,7 +11,7 @@ keywords = ["ui", "gui", "react", "ssr", "fullstack"]
 
 [dependencies]
 # server functions
-server_fn = { version = "0.4.6", default-features = false }
+server_fn = { version = "0.5.2", default-features = false }
 dioxus_server_macro = { workspace = true }
 
 # warp

+ 1 - 1
packages/fullstack/src/router.rs

@@ -53,7 +53,7 @@ fn default_external_navigation_handler() -> fn(Scope) -> Element {
     dioxus_router::prelude::FailureExternalNavigation
 }
 
-/// The configeration for the router
+/// The configuration for the router
 #[derive(Props, serde::Serialize, serde::Deserialize)]
 pub struct FullstackRouterConfig<R>
 where

+ 0 - 8
packages/fullstack/src/server_fn.rs

@@ -125,14 +125,6 @@ impl server_fn::ServerFunctionRegistry<()> for DioxusServerFnRegistry {
         }
     }
 
-    fn register(
-        url: &'static str,
-        server_function: ServerFunction,
-        encoding: server_fn::Encoding,
-    ) -> Result<(), Self::Error> {
-        Self::register_explicit("", url, server_function, encoding)
-    }
-
     /// Returns the server function registered at the given URL, or `None` if no function is registered at that URL.
     fn get(url: &str) -> Option<server_fn::ServerFnTraitObj<()>> {
         REGISTERED_SERVER_FUNCTIONS

+ 1 - 1
packages/server-macro/Cargo.toml

@@ -17,7 +17,7 @@ proc-macro2 = "^1.0.63"
 quote = "^1.0.26"
 syn = { version = "2", features = ["full"] }
 convert_case = "^0.6.0"
-server_fn_macro = "^0.4.6"
+server_fn_macro = "^0.5.2"
 
 [lib]
 proc-macro = true

+ 1 - 1
packages/web/src/dom.rs

@@ -294,7 +294,7 @@ pub fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -
         "select" => Rc::new(SelectionData {}),
         "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)),
 
-        "scroll" => Rc::new(()),
+        "scroll" => Rc::new(ScrollData {}),
         "wheel" => Rc::new(WheelData::from(event)),
         "animationstart" | "animationend" | "animationiteration" => {
             Rc::new(AnimationData::from(event))