Add a ton of comments to rsx/hotreload, add snapshot testing, refactor a bit to simplify the crate (#2130)
Merge dynamic context and dynamic mapping, clean up the rsx hotreload logic, and add location data to the Rsx objects
This commit is contained in:
1 parent
b19a546c0a
commit
eb79e61642
31 files changed
+1413
-811
No files matched your search
@@ -0,0 +1,90 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use dioxus_rsx::{
|
||||
hot_reload::{diff_rsx, template_location, ChangedRsx, DiffResult},
|
||||
CallBody, HotReloadingContext,
|
||||
};
|
||||
use quote::quote;
|
||||
use syn::{parse::Parse, spanned::Spanned, File};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Mock;
|
||||
|
||||
impl HotReloadingContext for Mock {
|
||||
fn map_attribute(
|
||||
element_name_rust: &str,
|
||||
attribute_name_rust: &str,
|
||||
) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => match attribute_name_rust {
|
||||
"width" => Some(("width", Some("style"))),
|
||||
"height" => Some(("height", Some("style"))),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => Some(("svg", Some("svg"))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn testing_for_pattern() {
|
||||
let old = quote! {
|
||||
div {
|
||||
for item in vec![1, 2, 3] {
|
||||
div { "123" }
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let new = quote! {
|
||||
div {
|
||||
for item in vec![1, 2, 3] {
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let old: CallBody = syn::parse2(old).unwrap();
|
||||
let new: CallBody = syn::parse2(new).unwrap();
|
||||
|
||||
let updated = old.update_template::<Mock>(Some(new), "testing");
|
||||
|
||||
// currently, modifying a for loop is not hot reloadable
|
||||
// We want to change this...
|
||||
assert!(updated.is_none());
|
||||
|
||||
// let updated = old.update_template::<Mock>(Some(new), "testing").unwrap();
|
||||
|
||||
// let old = include_str!(concat!("./valid/for_.old.rsx"));
|
||||
// let new = include_str!(concat!("./valid/for_.new.rsx"));
|
||||
// let (old, new) = load_files(old, new);
|
||||
|
||||
// let DiffResult::RsxChanged { rsx_calls } = diff_rsx(&new, &old) else {
|
||||
// panic!("Expected a rsx call to be changed")
|
||||
// };
|
||||
|
||||
// for calls in rsx_calls {
|
||||
// let ChangedRsx { old, new } = calls;
|
||||
|
||||
// let old_start = old.span().start();
|
||||
|
||||
// let old_call_body = syn::parse2::<CallBody>(old.tokens).unwrap();
|
||||
// let new_call_body = syn::parse2::<CallBody>(new).unwrap();
|
||||
|
||||
// let leaked_location = Box::leak(template_location(old_start, file).into_boxed_str());
|
||||
|
||||
// let hotreloadable_template =
|
||||
// new_call_body.update_template::<Ctx>(Some(old_call_body), leaked_location);
|
||||
|
||||
// dbg!(hotreloadable_template);
|
||||
// }
|
||||
|
||||
// dbg!(rsx_calls);
|
||||
}
|
||||
@@ -1,53 +1,49 @@
|
||||
use dioxus_rsx::hot_reload::{diff_rsx, DiffResult};
|
||||
use syn::File;
|
||||
|
||||
macro_rules! assert_rsx_changed {
|
||||
(
|
||||
$( #[doc = $doc:expr] )*
|
||||
$name:ident
|
||||
) => {
|
||||
$( #[doc = $doc] )*
|
||||
#[test]
|
||||
fn $name() {
|
||||
let old = include_str!(concat!("./valid/", stringify!($name), ".old.rsx"));
|
||||
let new = include_str!(concat!("./valid/", stringify!($name), ".new.rsx"));
|
||||
let (old, new) = load_files(old, new);
|
||||
assert!(matches!( diff_rsx(&new, &old), DiffResult::RsxChanged { .. }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_code_changed {
|
||||
(
|
||||
$( #[doc = $doc:expr] )*
|
||||
$name:ident
|
||||
) => {
|
||||
$( #[doc = $doc] )*
|
||||
#[test]
|
||||
fn $name() {
|
||||
let old = include_str!(concat!("./invalid/", stringify!($name), ".old.rsx"));
|
||||
let new = include_str!(concat!("./invalid/", stringify!($name), ".new.rsx"));
|
||||
let (old, new) = load_files(old, new);
|
||||
assert!(matches!(diff_rsx(&new, &old), DiffResult::CodeChanged(_) ));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn load_files(old: &str, new: &str) -> (File, File) {
|
||||
let old = syn::parse_file(old).unwrap();
|
||||
let new = syn::parse_file(new).unwrap();
|
||||
(old, new)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hotreloads() {
|
||||
let (old, new) = load_files(
|
||||
include_str!("./valid/expr.old.rsx"),
|
||||
include_str!("./valid/expr.new.rsx"),
|
||||
);
|
||||
assert_rsx_changed![combo];
|
||||
assert_rsx_changed![expr];
|
||||
assert_rsx_changed![for_];
|
||||
assert_rsx_changed![if_];
|
||||
assert_rsx_changed![let_];
|
||||
assert_rsx_changed![nested];
|
||||
|
||||
assert!(matches!(
|
||||
diff_rsx(&new, &old),
|
||||
DiffResult::RsxChanged { .. }
|
||||
));
|
||||
|
||||
let (old, new) = load_files(
|
||||
include_str!("./valid/let.old.rsx"),
|
||||
include_str!("./valid/let.new.rsx"),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
diff_rsx(&new, &old),
|
||||
DiffResult::RsxChanged { .. }
|
||||
));
|
||||
|
||||
let (old, new) = load_files(
|
||||
include_str!("./valid/combo.old.rsx"),
|
||||
include_str!("./valid/combo.new.rsx"),
|
||||
);
|
||||
|
||||
assert!(matches!(
|
||||
diff_rsx(&new, &old),
|
||||
DiffResult::RsxChanged { .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doesnt_hotreload() {
|
||||
let (old, new) = load_files(
|
||||
include_str!("./invalid/changedexpr.old.rsx"),
|
||||
include_str!("./invalid/changedexpr.new.rsx"),
|
||||
);
|
||||
|
||||
let res = diff_rsx(&new, &old);
|
||||
dbg!(&res);
|
||||
assert!(matches!(res, DiffResult::CodeChanged(_)));
|
||||
}
|
||||
assert_code_changed![changedexpr];
|
||||
@@ -0,0 +1,112 @@
|
||||
use dioxus_rsx::{CallBody, HotReloadingContext};
|
||||
use quote::quote;
|
||||
|
||||
struct Mock;
|
||||
|
||||
impl HotReloadingContext for Mock {
|
||||
fn map_attribute(
|
||||
element_name_rust: &str,
|
||||
attribute_name_rust: &str,
|
||||
) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => match attribute_name_rust {
|
||||
"width" => Some(("width", Some("style"))),
|
||||
"height" => Some(("height", Some("style"))),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_element(element_name_rust: &str) -> Option<(&'static str, Option<&'static str>)> {
|
||||
match element_name_rust {
|
||||
"svg" => Some(("svg", Some("svg"))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_template() {
|
||||
let input = quote! {
|
||||
svg {
|
||||
width: 100,
|
||||
height: "100px",
|
||||
"width2": 100,
|
||||
"height2": "100px",
|
||||
p { "hello world" }
|
||||
{(0..10).map(|i| rsx!{"{i}"})}
|
||||
}
|
||||
};
|
||||
|
||||
let call_body: CallBody = syn::parse2(input).unwrap();
|
||||
let new_template = call_body.update_template::<Mock>(None, "testing").unwrap();
|
||||
insta::assert_debug_snapshot!(new_template);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_template() {
|
||||
#[allow(unused, non_snake_case)]
|
||||
fn Comp() -> dioxus_core::Element {
|
||||
None
|
||||
}
|
||||
|
||||
let input = quote! {
|
||||
svg {
|
||||
width: 100,
|
||||
height: "100px",
|
||||
"width2": 100,
|
||||
"height2": "100px",
|
||||
p { "hello world" }
|
||||
{(0..10).map(|i| rsx!{"{i}"})},
|
||||
{(0..10).map(|i| rsx!{"{i}"})},
|
||||
{(0..11).map(|i| rsx!{"{i}"})},
|
||||
Comp {}
|
||||
}
|
||||
};
|
||||
|
||||
let call_body1: CallBody = syn::parse2(input).unwrap();
|
||||
let created_template = call_body1.update_template::<Mock>(None, "testing").unwrap();
|
||||
insta::assert_debug_snapshot!(created_template);
|
||||
|
||||
// scrambling the attributes should not cause a full rebuild
|
||||
let input = quote! {
|
||||
div {
|
||||
"width2": 100,
|
||||
height: "100px",
|
||||
"height2": "100px",
|
||||
width: 100,
|
||||
Comp {}
|
||||
{(0..11).map(|i| rsx!{"{i}"})},
|
||||
{(0..10).map(|i| rsx!{"{i}"})},
|
||||
{(0..10).map(|i| rsx!{"{i}"})},
|
||||
p {
|
||||
"hello world"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let call_body2: CallBody = syn::parse2(input).unwrap();
|
||||
let new_template = call_body2
|
||||
.update_template::<Mock>(Some(call_body1), "testing")
|
||||
.unwrap();
|
||||
|
||||
insta::assert_debug_snapshot!(new_template);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn changing_forloops_is_okay() {
|
||||
let input = quote! {
|
||||
div {
|
||||
for i in 0..10 {
|
||||
div { "123" }
|
||||
"asdasd"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let call_body: CallBody = syn::parse2(input).unwrap();
|
||||
let new_template = call_body.update_template::<Mock>(None, "testing").unwrap();
|
||||
|
||||
dbg!(new_template);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
source: packages/rsx/tests/rsx_diff.rs
|
||||
expression: template
|
||||
---
|
||||
Template {
|
||||
name: "testing",
|
||||
roots: [
|
||||
Element {
|
||||
tag: "svg",
|
||||
namespace: Some(
|
||||
"svg",
|
||||
),
|
||||
attrs: [
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
Static {
|
||||
name: "height",
|
||||
value: "100px",
|
||||
namespace: Some(
|
||||
"style",
|
||||
),
|
||||
},
|
||||
Dynamic {
|
||||
id: 1,
|
||||
},
|
||||
Static {
|
||||
name: "height2",
|
||||
value: "100px",
|
||||
namespace: None,
|
||||
},
|
||||
],
|
||||
children: [
|
||||
Element {
|
||||
tag: "p",
|
||||
namespace: None,
|
||||
attrs: [],
|
||||
children: [
|
||||
Text {
|
||||
text: "hello world",
|
||||
},
|
||||
],
|
||||
},
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
node_paths: [
|
||||
[
|
||||
0,
|
||||
1,
|
||||
],
|
||||
],
|
||||
attr_paths: [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
0,
|
||||
],
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
source: packages/rsx/tests/rsx_diff.rs
|
||||
expression: new_template
|
||||
---
|
||||
Template {
|
||||
name: "testing",
|
||||
roots: [
|
||||
Element {
|
||||
tag: "div",
|
||||
namespace: None,
|
||||
attrs: [
|
||||
Dynamic {
|
||||
id: 1,
|
||||
},
|
||||
Static {
|
||||
name: "height",
|
||||
value: "100px",
|
||||
namespace: None,
|
||||
},
|
||||
Static {
|
||||
name: "height2",
|
||||
value: "100px",
|
||||
namespace: None,
|
||||
},
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
],
|
||||
children: [
|
||||
Dynamic {
|
||||
id: 3,
|
||||
},
|
||||
Dynamic {
|
||||
id: 2,
|
||||
},
|
||||
Dynamic {
|
||||
id: 1,
|
||||
},
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
Element {
|
||||
tag: "p",
|
||||
namespace: None,
|
||||
attrs: [],
|
||||
children: [
|
||||
Text {
|
||||
text: "hello world",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
node_paths: [
|
||||
[
|
||||
0,
|
||||
3,
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
],
|
||||
],
|
||||
attr_paths: [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
0,
|
||||
],
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
source: packages/rsx/tests/rsx_diff.rs
|
||||
expression: created_template
|
||||
---
|
||||
Template {
|
||||
name: "testing",
|
||||
roots: [
|
||||
Element {
|
||||
tag: "svg",
|
||||
namespace: Some(
|
||||
"svg",
|
||||
),
|
||||
attrs: [
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
Static {
|
||||
name: "height",
|
||||
value: "100px",
|
||||
namespace: Some(
|
||||
"style",
|
||||
),
|
||||
},
|
||||
Dynamic {
|
||||
id: 1,
|
||||
},
|
||||
Static {
|
||||
name: "height2",
|
||||
value: "100px",
|
||||
namespace: None,
|
||||
},
|
||||
],
|
||||
children: [
|
||||
Element {
|
||||
tag: "p",
|
||||
namespace: None,
|
||||
attrs: [],
|
||||
children: [
|
||||
Text {
|
||||
text: "hello world",
|
||||
},
|
||||
],
|
||||
},
|
||||
Dynamic {
|
||||
id: 0,
|
||||
},
|
||||
Dynamic {
|
||||
id: 1,
|
||||
},
|
||||
Dynamic {
|
||||
id: 2,
|
||||
},
|
||||
Dynamic {
|
||||
id: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
node_paths: [
|
||||
[
|
||||
0,
|
||||
1,
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
],
|
||||
[
|
||||
0,
|
||||
3,
|
||||
],
|
||||
[
|
||||
0,
|
||||
4,
|
||||
],
|
||||
],
|
||||
attr_paths: [
|
||||
[
|
||||
0,
|
||||
],
|
||||
[
|
||||
0,
|
||||
],
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
for items in vec![1, 2, 3] {
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
for items in vec![1, 2, 3] {
|
||||
div { "123" }
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
if cond() {
|
||||
div { "123" }
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
if cond() {
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,10 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
ForLoop {
|
||||
div { "123" }
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
pub fn CoolChild() -> Element {
|
||||
rsx! {
|
||||
ForLoop {
|
||||
div { "asasddasdasd" }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user