Rewrite all the `dioxus_elements` logic for more modular `hotreload` support
It is very much a draft. For now, this channel is mainly a way for me to write down my reasoning about what has to be done.
The final goal of this PR is to get to a state where:
- the knowledge about the elements is not baked into the cli
- the user can easily add or change (and maybe delete) the elements we wants to use in the rsx
- the rsx hotreload and the compilation does not take more time
- maybe use a trait-based element definition to allow the user to add it's own element without having
dioxus_elementsin scope - the code for hotreload is simpler (no
HotReloadCtxtrait)
I also have a different PR #3274 that generates the elements spec with codegen, and this work is almost usable right now. I will have to merge the 2 at one point.
ROADMAP:
-
in
dioxus_core, define the struct for an element specification -
in manganis, define a way to register elements
- create a
Metadatastruct that allows to pass arbitrary strings instead of assets (https://github.com/rambip/dioxus/blob/elements_rewrite/packages/manganis/manganis-core/src/metadata.rs) - add a macro that can register metadata (strings should be enough) (https://github.com/rambip/dioxus/blob/elements_rewrite/packages/manganis/manganis-macro/src/metadata.rs)
- create a
-
in dioxus_html and dioxus_freya, use traits or modules to define the elements (that way you get compile-time goodness) https://github.com/rambip/dioxus/blob/elements_rewrite/packages/html/src/elements.rs
- use register_element! so that all elements are baked somewhere inside the dioxus app
-
in dioxus_cli, extract the element specs from the dioxus app with manganis.
- https://github.com/rambip/dioxus/blob/elements_rewrite/packages/cli/src/elements_metadata.rs
- Merge all specs in one big struct. When there is hotreload, use this knowledge to update the app with signals
-
write an exemple of how to use it in your dioxus app to create a custom element
First difficulty: where must the element namespace be static, and when does it have to do dynamic ? Clearly, since the cli reads the element specification at runtime, it's element specifications are dynamic.
Right now, the fileds of TemplateNode for tag and namespace are static, and I think it will not work for our approach.
We could Box::leak it but it's not very elegant.
Right now, the hotreloading is enabled by the following trait (https://github.com/DioxusLabs/dioxus/blob/main/packages/core-types/src/hr_context.rs):
pub trait HotReloadingContext {
fn map_attribute(
element_name_rust: &str,
attribute_name_rust: &str,
) -> Option<(&'static str, Option<&'static str>)>;
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>;
}
Meaning that the namespace of the attribute depends on the parent element. If we have to call manganis::register_attribute for each attribute inside each element, the amount of static data generated will be huge.
I have to find a way to compress it, like pointing to an attribute inside the definition of an element
First difficulty: where must the element namespace be static, and when does it have to do dynamic ? Clearly, since the cli reads the element specification at runtime, it's element specifications are dynamic.
Right now, the fields of
TemplateNodefortagandnamespaceare static, and I think it will not work for our approach. We couldBox::leakit but it's not very elegant.
We currently leak the string in debug mode during hot reloading. Templates are fairly small and the leak only happens in debug mode, so it hasn't been an issue so far, but we have also discussed switching to Cow<'static, str> in the future. That would be a much more broad breaking change that will require a lot of changes to the core crate and tests. I think it would be better to just continue leaking for now to keep the scope of this PR more manageable.
Currently, the CLI sends a HotReloadedTemplate message to the front end for hot reloading. That is then combined with the dynamic pool to create a full VNode
Right now, the hotreloading is enabled by the following trait (https://github.com/DioxusLabs/dioxus/blob/main/packages/core-types/src/hr_context.rs):
pub trait HotReloadingContext { fn map_attribute( element_name_rust: &str, attribute_name_rust: &str, ) -> Option<(&'static str, Option<&'static str>)>; fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)>; }Meaning that the namespace of the attribute depends on the parent element. If we have to call
manganis::register_attributefor each attribute inside each element, the amount of static data generated will be huge.I have to find a way to compress it, like pointing to an attribute inside the definition of an element
The svg and global attributes traits could be pulled out into a separate "attribute group" section with a name and then each element could include one or more attribute group. For example, a td element would include colspan, and rowspan as special attributes and the string, or hash of GlobalAttribute for the global attribute group
Ok so something like
manganis::register_element(name_of_element, namespace, list_of_attributes, optional_attribute_group)manganis::register_attribute_group(name_of_attribute_group, list_of_attributes)
https://github.com/DioxusLabs/dioxus/blob/793f833bb0714034d93f938a9c918fd894a3364a/packages/core-types/src/elements.rs#L17
I think I will split the macro functionality into 2:
manganis::metadata!(name, key)- a
#[derive(dioxus::ElementSpecification)]as a standard way for the user to define an element. I have to define the syntax though. Something like:
#[derive(dioxus::ElementSpecification)]
mod div {
static NAME_SPACE: &'static str = "div";
static TAG_NAME: Option<&'static str> = None;
use x::*;
static x: Attribute = (...);
}
#[derive(dioxus::AttributeGroupSpecification)]
mod svg_element {
static x: Attribute = (...);
}
I have seen that the manganis support crate for the cli has moved to https://github.com/DioxusLabs/dioxus/blob/main/packages/cli-opt/src/lib.rs
Is renaming AssetManifest to ManganisData and adding a metadata field in this file ok ?
I don't know if the metadata (where the element specification will be stored) have to be read each time the assets have to be read.
I have seen that the manganis support crate for the cli has moved to https://github.com/DioxusLabs/dioxus/blob/main/packages/cli-opt/src/lib.rs
Is renaming
AssetManifesttoManganisDataand adding ametadatafield in this file ok ?
From the linker side, it might be easier to model the metadata as a separate variant of the asset enum so we can re-use all of the extraction logic. After we get the data out of the link sections splitting it into a separate field would be good. This PR will already be breaking, so changing public fields isn't an issue. The name ManganisData is a bit more vague than AssetManifest and you could say the rsx macro definition is an asset provided to the CLI. Renaming things should be easy enough with rust analyzer once the rest of the PR is working
I don't know if the metadata (where the element specification will be stored) have to be read each time the assets have to be read.
I think both metadata and assets will need to be extracted every time the build runs before serve finishes, so they can share the same link section
I have seen that the manganis support crate for the cli has moved to https://github.com/DioxusLabs/dioxus/blob/main/packages/cli-opt/src/lib.rs Is renaming
AssetManifesttoManganisDataand adding ametadatafield in this file ok ?From the linker side, it might be easier to model the metadata as a separate variant of the asset enum so we can re-use all of the extraction logic. After we get the data out of the link sections splitting it into a separate field would be good. This PR will already be breaking, so changing public fields isn't an issue. The name
ManganisDatais a bit more vague thanAssetManifestand you could say the rsx macro definition is an asset provided to the CLI. Renaming things should be easy enough with rust analyzer once the rest of the PR is workingI don't know if the metadata (where the element specification will be stored) have to be read each time the assets have to be read.
I think both metadata and assets will need to be extracted every time the build runs before serve finishes, so they can share the same link section
But an important property of an asset is it's path, what path do I set for metadata ? The current rust file ?
But an important property of an asset is it's path, what path do I set for metadata ? The current rust file ?
Sorry, I was thinking of the old structure of manganis. Path used to be optional. It could be a different link section or a different top level enum that is either an asset or metadata.
Hi there, just closing this since it's been up for a while. Please feel free to re-open it or make a new PR if anything updates here!