Jonathan Kelley 3 лет назад
Родитель
Сommit
bd341f5

+ 42 - 41
.github/workflows/windows.yml

@@ -19,52 +19,53 @@ jobs:
       max-parallel: 2
       fail-fast: false
       matrix:
-        target: [
-          i686-pc-windows-gnu,
-          i686-pc-windows-msvc,
-          x86_64-pc-windows-gnu,
-          x86_64-pc-windows-msvc,
-        ]
+        target:
+          [
+            i686-pc-windows-gnu,
+            i686-pc-windows-msvc,
+            x86_64-pc-windows-gnu,
+            x86_64-pc-windows-msvc,
+          ]
         cfg_release_channel: [nightly, stable]
 
     steps:
-    # The Windows runners have autocrlf enabled by default
-    # which causes failures for some of rustfmt's line-ending sensitive tests
-    - name: disable git eol translation
-      run: git config --global core.autocrlf false
-    - name: checkout
-      uses: actions/checkout@v2
+      # The Windows runners have autocrlf enabled by default
+      # which causes failures for some of rustfmt's line-ending sensitive tests
+      - name: disable git eol translation
+        run: git config --global core.autocrlf false
+      - name: checkout
+        uses: actions/checkout@v2
 
-      # Run build
-    - name: Install Rustup using win.rustup.rs
-      run: |
-        # Disable the download progress bar which can cause perf issues
-        $ProgressPreference = "SilentlyContinue"
-        Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
-        .\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none
-        del rustup-init.exe
-        rustup target add ${{ matrix.target }}
-      shell: powershell
+        # Run build
+      - name: Install Rustup using win.rustup.rs
+        run: |
+          # Disable the download progress bar which can cause perf issues
+          $ProgressPreference = "SilentlyContinue"
+          Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
+          .\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --default-toolchain=none
+          del rustup-init.exe
+          rustup target add ${{ matrix.target }}
+        shell: powershell
 
-    - name: Add mingw32 to path for i686-gnu
-      run: |
-        echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
-      if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly'
-      shell: bash
+      - name: Add mingw32 to path for i686-gnu
+        run: |
+          echo "C:\msys64\mingw32\bin" >> $GITHUB_PATH
+        if: matrix.target == 'i686-pc-windows-gnu' && matrix.channel == 'nightly'
+        shell: bash
 
-    - name: Add mingw64 to path for x86_64-gnu
-      run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
-      if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly'
-      shell: bash
+      - name: Add mingw64 to path for x86_64-gnu
+        run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH
+        if: matrix.target == 'x86_64-pc-windows-gnu' && matrix.channel == 'nightly'
+        shell: bash
 
-    - name: build
-      run: |
-        rustc -Vv
-        cargo -V
-        set RUST_BACKTRACE=1 
-        cargo build
-      shell: cmd
+      - name: build
+        run: |
+          rustc -Vv
+          cargo -V
+          set RUST_BACKTRACE=1 
+          cargo build --features "desktop, ssr, router"
+        shell: cmd
 
-    - name: test
-      run: cargo test
-      shell: cmd
+      - name: test
+        run: cargo test
+        shell: cmd

+ 2 - 2
examples/framework_benchmark.rs

@@ -89,8 +89,8 @@ fn app(cx: Scope) -> Element {
 
 #[derive(Props)]
 struct ActionButtonProps<'a> {
-    name: &'static str,
-    id: &'static str,
+    name: &'a str,
+    id: &'a str,
     onclick: &'a dyn Fn(),
 }
 

+ 1 - 1
examples/inputs.rs

@@ -133,7 +133,7 @@ fn app(cx: Scope) -> Element {
                         r#type: "{field}",
                         value: "{value}",
                         // checked: "false",
-                        oninput: move |evt: Arc<FormEvent>| {
+                        oninput: move |evt: FormEvent| {
                             println!("{:?}", evt);
                         },
                     }

+ 1 - 1
examples/manual_edits.rs

@@ -35,5 +35,5 @@ fn main() {
 
     let app: Component = |cx| cx.render(rsx!(div { "some app" }));
 
-    dioxus_desktop::launch_core(app, (), |c| c.with_edits(edits));
+    dioxus_desktop::launch_with_props(app, (), |c| c.with_edits(edits));
 }

+ 3 - 3
examples/pattern_model.rs

@@ -116,8 +116,8 @@ fn app(cx: Scope) -> Element {
 
 #[derive(Props)]
 struct CalculatorKeyProps<'a> {
-    name: &'static str,
-    onclick: &'a dyn Fn(Arc<MouseEvent>),
+    name: &'a str,
+    onclick: &'a dyn Fn(MouseEvent),
     children: Element<'a>,
 }
 
@@ -211,7 +211,7 @@ impl Calculator {
         self.cur_val = self.display_value.parse::<f64>().unwrap();
         self.waiting_for_operand = true;
     }
-    fn handle_keydown(&mut self, evt: Arc<KeyboardEvent>) {
+    fn handle_keydown(&mut self, evt: KeyboardEvent) {
         match evt.key_code {
             KeyCode::Backspace => self.backspace(),
             KeyCode::Num0 => self.input_digit(0),

+ 40 - 51
examples/router.rs

@@ -1,64 +1,53 @@
-use dioxus::prelude::*;
-use dioxus_router::{use_router, Link};
-
-#[derive(PartialEq, Debug, Clone)]
-pub enum Route {
-    // #[at("/")]
-    Home,
-
-    // #[at("/:id")]
-    AllUsers { page: u32 },
-
-    // #[at("/:id")]
-    User { id: u32 },
+#![allow(non_snake_case)]
 
-    // #[at("/:id")]
-    BlogList { page: u32 },
-
-    // #[at("/:id")]
-    BlogPost { post_id: u32 },
+use dioxus::prelude::*;
+use dioxus::router::{Link, Route, Router};
 
-    // #[at("/404")]
-    // #[not_found]
-    NotFound,
+fn main() {
+    dioxus::desktop::launch(app);
 }
 
 fn app(cx: Scope) -> Element {
-    let route = use_router(&cx, Route::parse);
-
     cx.render(rsx! {
-        nav {
-            Link { to: Route::Home, "Go home!" }
-            Link { to: Route::AllUsers { page: 0 }, "List all users" }
-            Link { to: Route::BlogList { page: 0 }, "Blog posts" }
-        }
-        match route {
-            Route::Home => rsx!("Home"),
-            Route::AllUsers { page } => rsx!("All users - page {page}"),
-            Route::User { id } => rsx!("User - id: {id}"),
-            Route::BlogList { page } => rsx!("Blog posts - page {page}"),
-            Route::BlogPost { post_id } => rsx!("Blog post - post {post_id}"),
-            Route::NotFound => rsx!("Not found"),
+        Router {
+            ul {
+                Link { to: "/",  li { "Go home!" } }
+                Link { to: "users",  li { "List all users" } }
+                Link { to: "blog", li { "Blog posts" } }
+            }
+            Route { to: "/", "Home" }
+            Route { to: "users",
+                Route { to: "/", "User list" }
+                Route { to: ":name", BlogPost {} }
+             }
+            Route { to: "blog"
+                Route { to: "/", "Blog list" }
+                Route { to: ":post", BlogPost {} }
+            }
+            Route { to: "", "Err 404 Route Not Found" }
         }
-        footer {}
     })
 }
 
-impl Route {
-    // Generate the appropriate route from the "tail" end of the URL
-    fn parse(url: &str) -> Self {
-        use Route::*;
-        match url {
-            "/" => Home,
-            "/users" => AllUsers { page: 1 },
-            "/users/:page" => AllUsers { page: 1 },
-            "/users/:page/:id" => User { id: 1 },
-            "/blog" => BlogList { page: 1 },
-            "/blog/:page" => BlogList { page: 1 },
-            "/blog/:page/:id" => BlogPost { post_id: 1 },
-            _ => NotFound,
+fn BlogPost(cx: Scope) -> Element {
+    let post = dioxus::router::use_route(&cx).last_segment()?;
+
+    cx.render(rsx! {
+        div {
+            h1 { "Reading blog post: {post}" }
+            p { "example blog post" }
         }
-    }
+    })
 }
 
-fn main() {}
+fn User(cx: Scope) -> Element {
+    let post = dioxus::router::use_route(&cx).last_segment()?;
+    let bold = dioxus::router::use_route(&cx).param::<bool>("bold");
+
+    cx.render(rsx! {
+        div {
+            h1 { "Reading blog post: {post}" }
+            p { "example blog post" }
+        }
+    })
+}

+ 22 - 2
packages/core-macro/src/rsx/component.rs

@@ -187,8 +187,12 @@ impl Parse for ComponentField {
         }
 
         if input.peek(LitStr) && input.peek2(Token![,]) {
-            let content = ContentField::Formatted(input.parse()?);
-            return Ok(Self { name, content });
+            let t: LitStr = input.fork().parse()?;
+
+            if is_literal_foramtted(&t) {
+                let content = ContentField::Formatted(input.parse()?);
+                return Ok(Self { name, content });
+            }
         }
 
         if input.peek(LitStr) && input.peek2(LitStr) {
@@ -209,3 +213,19 @@ impl ToTokens for ComponentField {
         })
     }
 }
+
+fn is_literal_foramtted(lit: &LitStr) -> bool {
+    let s = lit.value();
+    let mut chars = s.chars();
+
+    while let Some(next) = chars.next() {
+        if next == '{' {
+            let nen = chars.next();
+            if nen == Some('{') {
+                return true;
+            }
+        }
+    }
+
+    false
+}

+ 7 - 1
packages/core/src/events.rs

@@ -4,7 +4,7 @@
 //! This is all kinda WIP, but the bones are there.
 
 use crate::{ElementId, ScopeId};
-use std::{any::Any, cell::Cell, rc::Rc, sync::Arc};
+use std::{any::Any, cell::Cell, fmt::Debug, rc::Rc, sync::Arc};
 
 pub(crate) struct BubbleState {
     pub canceled: Cell<bool>,
@@ -143,6 +143,12 @@ pub struct UiEvent<T> {
     bubble_state: Rc<BubbleState>,
 }
 
+impl<T: Debug> std::fmt::Debug for UiEvent<T> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("UiEvent").field("data", &self.data).finish()
+    }
+}
+
 impl<T> std::ops::Deref for UiEvent<T> {
     type Target = T;
 

+ 0 - 1
packages/desktop/src/lib.rs

@@ -66,7 +66,6 @@ use tao::{
     event::{Event, StartCause, WindowEvent},
     event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
     keyboard::{KeyCode, ModifiersState},
-    menu::{MenuBar, MenuItem},
     window::{Window, WindowId},
 };
 pub use wry;

+ 1 - 1
packages/router/src/components/link.rs

@@ -38,7 +38,7 @@ pub struct LinkProps<'a> {
 }
 
 pub fn Link<'a>(cx: Scope<'a, LinkProps<'a>>) -> Element {
-    let service = cx.consume_context::<RouterService>().unwrap();
+    let service = cx.consume_context::<RouterService>()?;
     cx.render(rsx! {
         a {
             href: "{cx.props.to}",

+ 2 - 2
packages/router/src/hooks/use_route.rs

@@ -15,7 +15,7 @@ impl<'a> UseRoute<'a> {
         todo!()
     }
 
-    pub fn last_segment(&self) -> Option<&str> {
+    pub fn last_segment(&self) -> Option<&'a str> {
         todo!()
     }
 
@@ -25,6 +25,6 @@ impl<'a> UseRoute<'a> {
     }
 }
 
-pub fn use_route(cx: &ScopeState) -> UseRoute {
+pub fn use_route<'a>(cx: &'a ScopeState) -> UseRoute<'a> {
     todo!()
 }

+ 1 - 1
packages/ssr/Cargo.toml

@@ -19,6 +19,7 @@ dioxus-core = { path = "../core", version = "^0.1.6", features = ["serialize"] }
 [dev-dependencies]
 dioxus-hooks = { path = "../hooks" }
 dioxus-html = { path = "../html" }
+dioxus-core-macro = { path = "../core-macro" }
 thiserror = "1.0.23"
 log = "0.4.13"
 fern = { version = "0.6.0", features = ["colored"] }
@@ -27,4 +28,3 @@ argh = "0.1.4"
 serde = "1.0.120"
 serde_json = "1.0.61"
 fs_extra = "1.2.0"
-dioxus-core-macro = { path = "../core-macro" }

+ 9 - 9
packages/ssr/README.md

@@ -17,7 +17,7 @@ This crate is a part of the broader Dioxus ecosystem. For more resources about D
 
 Dioxus SSR provides utilities to render Dioxus components to valid HTML. Once rendered, the HTML can be rehydrated client side or served from your web-server of choice.
 
-```rust
+```rust, ignore
 let app: Component = |cx| cx.render(rsx!(div {"hello world!"}));
 
 let mut vdom = VirtualDom::new(app);
@@ -32,7 +32,7 @@ assert_eq!(text, "<div>hello world!</div>")
 
 The simplest example is to simply render some `rsx!` nodes to html. This can be done with the [`render_lazy`] api.
 
-```rust
+```rust, ignore
 let content = dioxus::ssr::render(rsx!{
     div {
         (0..5).map(|i| rsx!(
@@ -44,7 +44,7 @@ let content = dioxus::ssr::render(rsx!{
 
 ## Rendering a VirtualDom
 
-```rust
+```rust, ignore
 let mut dom = VirtualDom::new(app);
 let _ = dom.rebuild();
 
@@ -54,7 +54,7 @@ let content = dioxus::ssr::render_vdom(&dom);
 ## Configuring output
 It's possible to configure the output of the generated HTML. 
 
-```rust
+```rust, ignore
 let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).prerender(true));
 ```
 
@@ -62,7 +62,7 @@ let content = dioxus::ssr::render_vdom(&dom, |config| config.pretty(true).preren
 
 We provide the basic `SsrFormatter` object that implements `Display`, so you can integrate SSR into an existing string, or write directly to a file.
 
-```rust
+```rust, ignore
 use std::fmt::{Error, Write};
 
 let mut buf = String::new();
@@ -92,7 +92,7 @@ With pre-rendering enabled, this crate will generate element nodes with Element
 
 To enable pre-rendering, simply configure the `SsrConfig` with pre-rendering enabled.
 
-```rust
+```rust, ignore
 let dom = VirtualDom::new(App);
 
 let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
@@ -102,7 +102,7 @@ let text = dioxus::ssr::render_vdom(App, |cfg| cfg.pre_render(true));
 
 Dioxus SSR can also be to render on the server. Obviously, you can just render the VirtualDOM to a string and send that down.
 
-```rust
+```rust, ignore
 let text = dioxus::ssr::render_vdom(&vdom);
 assert_eq!(text, "<div>hello world!</div>")
 ```
@@ -113,7 +113,7 @@ The rest of the space - IE doing this more efficiently, caching the virtualdom,
 
 Dioxus SSR needs an arena to allocate from - whether it be the VirtualDom or a dedicated Bump allocator. To render `rsx!` directly to a string, you'll want to create an `SsrRenderer` and call `render_lazy`.
 
-```rust
+```rust, ignore
 let text = dioxus::ssr::SsrRenderer::new().render_lazy(rsx!{
     div { "hello world" }
 });
@@ -122,7 +122,7 @@ assert_eq!(text, "<div>hello world!</div>")
 
 This can be automated with the `render_lazy!` macro:
 
-```rust
+```rust, ignore
 let text = render_lazy!(rsx!( div { "hello world" } ));
 ```
 

+ 1 - 1
packages/ssr/index.html

@@ -1 +1 @@
-asd
+<div title="About W3Schools"><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 0</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 1</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 2</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 3</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 4</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 5</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 6</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 7</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 8</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 9</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 10</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 11</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 12</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 13</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 14</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 15</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 16</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 17</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 18</p></div><div title="About W3Schools" style="color:blue;text-align:center" class="About W3Schools"><p title="About W3Schools">Hello world!: 19</p></div></div>

+ 12 - 168
packages/ssr/src/lib.rs

@@ -2,13 +2,15 @@
 
 use std::fmt::{Display, Formatter};
 
-use dioxus_core::exports::bumpalo;
-use dioxus_core::exports::bumpalo::Bump;
 use dioxus_core::IntoVNode;
 use dioxus_core::*;
 
+fn app(_cx: Scope) -> Element {
+    None
+}
+
 pub struct SsrRenderer {
-    inner: bumpalo::Bump,
+    vdom: VirtualDom,
     cfg: SsrConfig,
 }
 
@@ -16,21 +18,14 @@ impl SsrRenderer {
     pub fn new(cfg: impl FnOnce(SsrConfig) -> SsrConfig) -> Self {
         Self {
             cfg: cfg(SsrConfig::default()),
-            inner: bumpalo::Bump::new(),
+            vdom: VirtualDom::new(app),
         }
     }
 
     pub fn render_lazy<'a>(&'a mut self, f: LazyNodes<'a, '_>) -> String {
-        let bump = &mut self.inner as *mut _;
-        let s = self.render_inner(f);
+        let scope = self.vdom.base_scope();
+        let factory = NodeFactory::new(&scope);
 
-        // reuse the bump's memory
-        unsafe { (&mut *bump as &mut bumpalo::Bump).reset() };
-        s
-    }
-
-    fn render_inner<'a>(&'a self, f: LazyNodes<'a, '_>) -> String {
-        let factory = NodeFactory::new(&self.inner);
         let root = f.into_vnode(factory);
         format!(
             "{:}",
@@ -44,8 +39,8 @@ impl SsrRenderer {
 }
 
 pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
-    let bump = bumpalo::Bump::new();
-    let borrowed = &bump;
+    let vdom = VirtualDom::new(app);
+    let scope: *const ScopeState = vdom.base_scope();
 
     // Safety
     //
@@ -57,9 +52,9 @@ pub fn render_lazy<'a>(f: LazyNodes<'a, '_>) -> String {
     //
     // Therefore, we cast our local bump alloactor into right lifetime. This is okay because our usage of the bump arena
     // is *definitely* shorter than the <'a> lifetime, and we return *owned* data - not borrowed data.
+    let scope = unsafe { &*scope };
 
-    let _b = unsafe { std::mem::transmute::<&Bump, &'a Bump>(borrowed) };
-    let root = f.into_vnode(NodeFactory::new(_b));
+    let root = f.into_vnode(NodeFactory::new(&scope));
 
     format!(
         "{:}",
@@ -282,154 +277,3 @@ impl SsrConfig {
         self
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use dioxus_core::prelude::*;
-    use dioxus_core_macro::*;
-    use dioxus_html as dioxus_elements;
-
-    static SIMPLE_APP: Component = |cx| {
-        cx.render(rsx!(div {
-            "hello world!"
-        }))
-    };
-
-    static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
-        cx.render(rsx! {
-            div { title: "About W3Schools",
-                (0..20).map(|f| rsx!{
-                    div {
-                        title: "About W3Schools",
-                        style: "color:blue;text-align:center",
-                        class: "About W3Schools",
-                        p {
-                            title: "About W3Schools",
-                            "Hello world!: {f}"
-                        }
-                    }
-                })
-            }
-        })
-    };
-
-    static NESTED_APP: Component = |cx| {
-        cx.render(rsx!(
-            div {
-                SIMPLE_APP {}
-            }
-        ))
-    };
-    static FRAGMENT_APP: Component = |cx| {
-        cx.render(rsx!(
-            div { "f1" }
-            div { "f2" }
-            div { "f3" }
-            div { "f4" }
-        ))
-    };
-
-    #[test]
-    fn to_string_works() {
-        let mut dom = VirtualDom::new(SIMPLE_APP);
-        dom.rebuild();
-        dbg!(render_vdom(&dom));
-    }
-
-    #[test]
-    fn hydration() {
-        let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild();
-        dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true)));
-    }
-
-    #[test]
-    fn nested() {
-        let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild();
-        dbg!(render_vdom(&dom));
-    }
-
-    #[test]
-    fn fragment_app() {
-        let mut dom = VirtualDom::new(FRAGMENT_APP);
-        dom.rebuild();
-        dbg!(render_vdom(&dom));
-    }
-
-    #[test]
-    fn write_to_file() {
-        use std::fs::File;
-        use std::io::Write;
-
-        let mut file = File::create("index.html").unwrap();
-
-        let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
-        dom.rebuild();
-
-        file.write_fmt(format_args!(
-            "{}",
-            TextRenderer::from_vdom(&dom, SsrConfig::default())
-        ))
-        .unwrap();
-    }
-
-    #[test]
-    fn styles() {
-        static STLYE_APP: Component = |cx| {
-            cx.render(rsx! {
-                div { color: "blue", font_size: "46px"  }
-            })
-        };
-
-        let mut dom = VirtualDom::new(STLYE_APP);
-        dom.rebuild();
-        dbg!(render_vdom(&dom));
-    }
-
-    #[test]
-    fn lazy() {
-        let p1 = SsrRenderer::new(|c| c).render_lazy(rsx! {
-            div { "ello"  }
-        });
-
-        let p2 = render_lazy(rsx! {
-            div {
-                "ello"
-            }
-        });
-        assert_eq!(p1, p2);
-    }
-
-    #[test]
-    fn big_lazy() {
-        let s = render_lazy(rsx! {
-            div {
-                div {
-                    div {
-                        h1 { "ello world" }
-                        h1 { "ello world" }
-                        h1 { "ello world" }
-                        h1 { "ello world" }
-                        h1 { "ello world" }
-                    }
-                }
-            }
-        });
-
-        dbg!(s);
-    }
-
-    #[test]
-    fn inner_html() {
-        let s = render_lazy(rsx! {
-            div {
-                dangerous_inner_html: "<div> ack </div>"
-            }
-        });
-
-        dbg!(s);
-    }
-}

+ 146 - 0
packages/ssr/tests/renders.rs

@@ -0,0 +1,146 @@
+use dioxus_core::prelude::*;
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+use dioxus_ssr::{render_lazy, render_vdom, render_vdom_cfg, SsrConfig, SsrRenderer, TextRenderer};
+
+static SIMPLE_APP: Component = |cx| {
+    cx.render(rsx!(div {
+        "hello world!"
+    }))
+};
+
+static SLIGHTLY_MORE_COMPLEX: Component = |cx| {
+    cx.render(rsx! {
+        div { title: "About W3Schools",
+            (0..20).map(|f| rsx!{
+                div {
+                    title: "About W3Schools",
+                    style: "color:blue;text-align:center",
+                    class: "About W3Schools",
+                    p {
+                        title: "About W3Schools",
+                        "Hello world!: {f}"
+                    }
+                }
+            })
+        }
+    })
+};
+
+static NESTED_APP: Component = |cx| {
+    cx.render(rsx!(
+        div {
+            SIMPLE_APP {}
+        }
+    ))
+};
+static FRAGMENT_APP: Component = |cx| {
+    cx.render(rsx!(
+        div { "f1" }
+        div { "f2" }
+        div { "f3" }
+        div { "f4" }
+    ))
+};
+
+#[test]
+fn to_string_works() {
+    let mut dom = VirtualDom::new(SIMPLE_APP);
+    dom.rebuild();
+    dbg!(render_vdom(&dom));
+}
+
+#[test]
+fn hydration() {
+    let mut dom = VirtualDom::new(NESTED_APP);
+    dom.rebuild();
+    dbg!(render_vdom_cfg(&dom, |c| c.pre_render(true)));
+}
+
+#[test]
+fn nested() {
+    let mut dom = VirtualDom::new(NESTED_APP);
+    dom.rebuild();
+    dbg!(render_vdom(&dom));
+}
+
+#[test]
+fn fragment_app() {
+    let mut dom = VirtualDom::new(FRAGMENT_APP);
+    dom.rebuild();
+    dbg!(render_vdom(&dom));
+}
+
+#[test]
+fn write_to_file() {
+    use std::fs::File;
+    use std::io::Write;
+
+    let mut file = File::create("index.html").unwrap();
+
+    let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
+    dom.rebuild();
+
+    file.write_fmt(format_args!(
+        "{}",
+        TextRenderer::from_vdom(&dom, SsrConfig::default())
+    ))
+    .unwrap();
+}
+
+#[test]
+fn styles() {
+    static STLYE_APP: Component = |cx| {
+        cx.render(rsx! {
+            div { color: "blue", font_size: "46px"  }
+        })
+    };
+
+    let mut dom = VirtualDom::new(STLYE_APP);
+    dom.rebuild();
+    dbg!(render_vdom(&dom));
+}
+
+#[test]
+fn lazy() {
+    let p1 = SsrRenderer::new(|c| c).render_lazy(rsx! {
+        div { "ello"  }
+    });
+
+    let p2 = render_lazy(rsx! {
+        div {
+            "ello"
+        }
+    });
+    assert_eq!(p1, p2);
+}
+
+#[test]
+fn big_lazy() {
+    let s = render_lazy(rsx! {
+        div {
+            div {
+                div {
+                    h1 { "ello world" }
+                    h1 { "ello world" }
+                    h1 { "ello world" }
+                    h1 { "ello world" }
+                    h1 { "ello world" }
+                }
+            }
+        }
+    });
+
+    dbg!(s);
+}
+
+#[test]
+fn inner_html() {
+    let s = render_lazy(rsx! {
+        div {
+            dangerous_inner_html: "<div> ack </div>"
+        }
+    });
+
+    dbg!(s);
+}

+ 15 - 15
src/lib.rs

@@ -39,7 +39,7 @@
 //!
 //! To launch an app, we use the `launch` method for the specific renderer we want to use. In the launch function, we pass the app's `Component`.
 //!
-//! ```rust
+//! ```rust, ignore
 //! use dioxus::prelude::*;
 //!
 //! fn main() {
@@ -62,7 +62,7 @@
 //! consistency, we force all attributes and listeners to be listed *before*
 //! children.
 //!
-//! ```rust
+//! ```rust, ignore
 //! let value = "123";
 //!
 //! rsx!(
@@ -78,7 +78,7 @@
 //! of the body as child elements and rust expressions. Any rust expression that
 //! implements `IntoIterator<Item = impl IntoVNode>` will be parsed as a child.
 //!
-//! ```rust
+//! ```rust, ignore
 //! rsx!(
 //!     div {
 //!         (0..10).map(|_| rsx!(span { "hello world" }))
@@ -94,7 +94,7 @@
 //! `cx` as the first argument of rsx. This is sometimes useful when we need to
 //! render nodes in match statements.
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn example(cx: Scope) -> Element {
 //!
 //!     // both of these are equivalent
@@ -107,7 +107,7 @@
 //! Putting everything together, we can write a simple component that a list of
 //! elements:
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn app(cx: Scope) -> Element {
 //!     let name = "dave";
 //!     cx.render(rsx!(
@@ -135,7 +135,7 @@
 //!
 //! In Dioxus, all properties are memoized by default!
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn App(cx: Scope) -> Element {
 //!     cx.render(rsx!(
 //!         Header {
@@ -149,7 +149,7 @@
 //! Our `Header` component takes in a `title` and a `color` property, which we
 //! delcare on an explicit `HeaderProps` struct.
 //!
-//! ```
+//! ```rust, ignore
 //! // The `Props` derive macro lets us add additional functionality to how props are interpreted.
 //! #[derive(Props, PartialEq)]
 //! struct HeaderProps {
@@ -170,7 +170,7 @@
 //! Components may use the `inline_props` macro to completely inline the props
 //! definition into the function arguments.
 //!
-//! ```rust
+//! ```rust, ignore
 //! #[inline_props]
 //! fn Header(cx: Scope, title: String, color: String) -> Element {
 //!     cx.render(rsx!(
@@ -186,7 +186,7 @@
 //! attach some lifetimes to the props struct.
 //! > Note: we don't need to derive `PartialEq` for borrowed props since they cannot be memoized.
 //!
-//! ```rust
+//! ```rust, ignore
 //! #[derive(Props)]
 //! struct HeaderProps<'a> {
 //!     title: &'a str,
@@ -206,7 +206,7 @@
 //! Components that beging with an uppercase letter may be called through
 //! traditional curly-brace syntax like so:
 //!
-//! ```rust
+//! ```rust, ignore
 //! rsx!(
 //!     Header { title: "My App" }
 //! )
@@ -215,7 +215,7 @@
 //! Alternatively, if your components begin with a lowercase letter, you can use
 //! the function call syntax:
 //!
-//! ```rust
+//! ```rust, ignore
 //! rsx!(
 //!     header( title: "My App" )
 //! )
@@ -230,7 +230,7 @@
 //! By convention, all hooks are functions that should start with `use_`. We can
 //! use hooks to define state and modify it from within listeners.
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn app(cx: Scope) -> Element {
 //!     let name = use_state(&cx, || "world");
 //!
@@ -250,7 +250,7 @@
 //!
 //! Most hooks you'll write are simply composition of other hooks:
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn use_username(cx: &ScopeState, id: Uuid) -> bool {
 //!     let users = use_context::<Users>(cx);
 //!     users.get(&id).map(|user| user.logged_in).ok_or(false)
@@ -259,7 +259,7 @@
 //!  
 //! To create entirely new foundational hooks, we can use the `use_hook` method on ScopeState.
 //!
-//! ```rust
+//! ```rust, ignore
 //! fn use_mut_string(cx: &ScopeState) -> &mut String {
 //!     cx.use_hook(|_| "Hello".to_string())
 //! }
@@ -271,7 +271,7 @@
 //!
 //! Using components, templates, and hooks, we can build a simple app.
 //!
-//! ```rust
+//! ```rust, ignore
 //! use dioxus::prelude::*;
 //!
 //! fn main() {