1#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
2#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
3#![deny(missing_docs)]
4
5pub use crate::cfg::Config;
8use crate::hydration::SuspenseMessage;
9use dioxus_core::{ScopeId, VirtualDom};
10use dom::WebsysDom;
11use futures_util::{FutureExt, StreamExt, pin_mut, select};
12
13mod cfg;
14mod dom;
15
16mod events;
17pub mod launch;
18mod mutations;
19pub use events::*;
20
21#[cfg(feature = "document")]
22mod document;
23#[cfg(feature = "document")]
24mod history;
25#[cfg(feature = "document")]
26pub use document::WebDocument;
27#[cfg(feature = "document")]
28pub use history::{HashHistory, WebHistory};
29
30mod files;
31pub use files::*;
32
33mod data_transfer;
34pub use data_transfer::*;
35
36#[cfg(all(feature = "devtools", debug_assertions))]
37mod devtools;
38
39mod hydration;
40#[allow(unused)]
41pub use hydration::*;
42
43pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
54 #[cfg(all(feature = "devtools", debug_assertions))]
55 let mut hotreload_rx = devtools::init(&web_config);
56
57 #[cfg(feature = "document")]
58 if let Some(history) = web_config.history.clone() {
59 virtual_dom.in_scope(ScopeId::ROOT, || dioxus_core::provide_context(history));
60 }
61
62 #[cfg(feature = "document")]
63 virtual_dom.in_runtime(document::init_document);
64
65 let runtime = virtual_dom.runtime();
66
67 let should_hydrate = web_config.hydrate || cfg!(feature = "hydrate");
69
70 let mut websys_dom = WebsysDom::new(web_config, runtime);
71
72 let mut hydration_receiver: Option<futures_channel::mpsc::UnboundedReceiver<SuspenseMessage>> =
73 None;
74
75 if should_hydrate {
76 #[cfg(all(feature = "devtools", debug_assertions))]
80 loop {
81 let mut timeout = gloo_timers::future::TimeoutFuture::new(100).fuse();
82 futures_util::select! {
83 msg = hotreload_rx.next() => {
84 if let Some(msg) = msg
85 && msg.for_build_id == Some(dioxus_cli_config::build_id()) {
86 dioxus_devtools::apply_changes(&virtual_dom, &msg);
87 }
88 }
89 _ = &mut timeout => {
90 break;
91 }
92 }
93 }
94
95 #[cfg(feature = "hydrate")]
96 {
97 use dioxus_fullstack_core::HydrationContext;
98
99 websys_dom.skip_mutations = true;
100 #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
102 export function get_initial_hydration_data() {
103 const decoded = atob(window.initial_dioxus_hydration_data);
104 return Uint8Array.from(decoded, (c) => c.charCodeAt(0))
105 }
106 export function get_initial_hydration_debug_types() {
107 return window.initial_dioxus_hydration_debug_types;
108 }
109 export function get_initial_hydration_debug_locations() {
110 return window.initial_dioxus_hydration_debug_locations;
111 }
112 "#)]
113 extern "C" {
114 fn get_initial_hydration_data() -> js_sys::Uint8Array;
115 fn get_initial_hydration_debug_types() -> Option<Vec<String>>;
116 fn get_initial_hydration_debug_locations() -> Option<Vec<String>>;
117 }
118 let hydration_data = get_initial_hydration_data().to_vec();
119
120 #[cfg(debug_assertions)]
122 let debug_types = get_initial_hydration_debug_types();
123 #[cfg(not(debug_assertions))]
124 let debug_types = None;
125 #[cfg(debug_assertions)]
126 let debug_locations = get_initial_hydration_debug_locations();
127 #[cfg(not(debug_assertions))]
128 let debug_locations = None;
129
130 let server_data =
131 HydrationContext::from_serialized(&hydration_data, debug_types, debug_locations);
132 if let Some(error) = server_data.error_entry().get().ok().flatten() {
134 virtual_dom.in_runtime(|| virtual_dom.runtime().throw_error(ScopeId::APP, error));
135 }
136 server_data.in_context(|| {
137 virtual_dom.in_scope(ScopeId::ROOT, || {
138 dioxus_core::provide_create_error_boundary(
140 dioxus_fullstack_core::init_error_boundary,
141 );
142 #[cfg(feature = "document")]
143 document::init_fullstack_document();
144 });
145 virtual_dom.rebuild(&mut websys_dom);
146 });
147 websys_dom.skip_mutations = false;
148
149 let rx = websys_dom.rehydrate(&virtual_dom).unwrap();
150 hydration_receiver = Some(rx);
151
152 #[cfg(feature = "mounted")]
153 {
154 websys_dom.flush_queued_mounted_events();
156 }
157 }
158 #[cfg(not(feature = "hydrate"))]
159 {
160 panic!("Hydration is not enabled. Please enable the `hydrate` feature.");
161 }
162 } else {
163 virtual_dom.rebuild(&mut websys_dom);
164
165 websys_dom.flush_edits();
166 }
167
168 loop {
169 #[cfg(all(feature = "devtools", debug_assertions))]
172 let template;
173 #[allow(unused)]
174 let mut hydration_work: Option<SuspenseMessage> = None;
175
176 {
177 let work = virtual_dom.wait_for_work().fuse();
178 pin_mut!(work);
179
180 let mut hydration_receiver_iter = futures_util::stream::iter(&mut hydration_receiver)
181 .fuse()
182 .flatten();
183 let mut rx_hydration = hydration_receiver_iter.select_next_some();
184
185 #[cfg(all(feature = "devtools", debug_assertions))]
186 #[allow(unused)]
187 {
188 let mut devtools_next = hotreload_rx.select_next_some();
189 select! {
190 _ = work => {
191 template = None;
192 },
193 new_template = devtools_next => {
194 template = Some(new_template);
195 },
196 hydration_data = rx_hydration => {
197 template = None;
198 #[cfg(feature = "hydrate")]
199 {
200 hydration_work = Some(hydration_data);
201 }
202 },
203 }
204 }
205
206 #[cfg(not(all(feature = "devtools", debug_assertions)))]
207 #[allow(unused)]
208 {
209 select! {
210 _ = work => {},
211 hyd = rx_hydration => {
212 #[cfg(feature = "hydrate")]
213 {
214 hydration_work = Some(hyd);
215 }
216 }
217 }
218 }
219 }
220
221 #[cfg(all(feature = "devtools", debug_assertions))]
222 if let Some(hr_msg) = template {
223 dioxus_devtools::apply_changes(&virtual_dom, &hr_msg);
225
226 if !hr_msg.assets.is_empty() {
227 crate::devtools::invalidate_browser_asset_cache();
228 }
229
230 if hr_msg.for_build_id == Some(dioxus_cli_config::build_id()) {
231 devtools::show_toast(
232 "Hot-patch success!",
233 &format!("App successfully patched in {} ms", hr_msg.ms_elapsed),
234 devtools::ToastLevel::Success,
235 std::time::Duration::from_millis(2000),
236 false,
237 );
238 }
239 }
240
241 #[cfg(feature = "hydrate")]
242 if let Some(hydration_data) = hydration_work {
243 websys_dom.rehydrate_streaming(hydration_data, &mut virtual_dom);
244 }
245
246 virtual_dom.render_immediate(&mut websys_dom);
259
260 websys_dom.flush_edits();
264 }
265}