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::{pin_mut, select, FutureExt, StreamExt};
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 if msg.for_build_id == Some(dioxus_cli_config::build_id()) {
86 dioxus_devtools::apply_changes(&virtual_dom, &msg);
87 }
88 }
89 }
90 _ = &mut timeout => {
91 break;
92 }
93 }
94 }
95
96 #[cfg(feature = "hydrate")]
97 {
98 use dioxus_fullstack_core::HydrationContext;
99
100 websys_dom.skip_mutations = true;
101 #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
103 export function get_initial_hydration_data() {
104 const decoded = atob(window.initial_dioxus_hydration_data);
105 return Uint8Array.from(decoded, (c) => c.charCodeAt(0))
106 }
107 export function get_initial_hydration_debug_types() {
108 return window.initial_dioxus_hydration_debug_types;
109 }
110 export function get_initial_hydration_debug_locations() {
111 return window.initial_dioxus_hydration_debug_locations;
112 }
113 "#)]
114 extern "C" {
115 fn get_initial_hydration_data() -> js_sys::Uint8Array;
116 fn get_initial_hydration_debug_types() -> Option<Vec<String>>;
117 fn get_initial_hydration_debug_locations() -> Option<Vec<String>>;
118 }
119 let hydration_data = get_initial_hydration_data().to_vec();
120
121 #[cfg(debug_assertions)]
123 let debug_types = get_initial_hydration_debug_types();
124 #[cfg(not(debug_assertions))]
125 let debug_types = None;
126 #[cfg(debug_assertions)]
127 let debug_locations = get_initial_hydration_debug_locations();
128 #[cfg(not(debug_assertions))]
129 let debug_locations = None;
130
131 let server_data =
132 HydrationContext::from_serialized(&hydration_data, debug_types, debug_locations);
133 if let Some(error) = server_data.error_entry().get().ok().flatten() {
135 virtual_dom.in_runtime(|| virtual_dom.runtime().throw_error(ScopeId::APP, error));
136 }
137 server_data.in_context(|| {
138 virtual_dom.in_scope(ScopeId::ROOT, || {
139 dioxus_core::provide_create_error_boundary(
141 dioxus_fullstack_core::init_error_boundary,
142 );
143 #[cfg(feature = "document")]
144 document::init_fullstack_document();
145 });
146 virtual_dom.rebuild(&mut websys_dom);
147 });
148 websys_dom.skip_mutations = false;
149
150 let rx = websys_dom.rehydrate(&virtual_dom).unwrap();
151 hydration_receiver = Some(rx);
152
153 #[cfg(feature = "mounted")]
154 {
155 websys_dom.flush_queued_mounted_events();
157 }
158 }
159 #[cfg(not(feature = "hydrate"))]
160 {
161 panic!("Hydration is not enabled. Please enable the `hydrate` feature.");
162 }
163 } else {
164 virtual_dom.rebuild(&mut websys_dom);
165
166 websys_dom.flush_edits();
167 }
168
169 loop {
170 #[cfg(all(feature = "devtools", debug_assertions))]
173 let template;
174 #[allow(unused)]
175 let mut hydration_work: Option<SuspenseMessage> = None;
176
177 {
178 let work = virtual_dom.wait_for_work().fuse();
179 pin_mut!(work);
180
181 let mut hydration_receiver_iter = futures_util::stream::iter(&mut hydration_receiver)
182 .fuse()
183 .flatten();
184 let mut rx_hydration = hydration_receiver_iter.select_next_some();
185
186 #[cfg(all(feature = "devtools", debug_assertions))]
187 #[allow(unused)]
188 {
189 let mut devtools_next = hotreload_rx.select_next_some();
190 select! {
191 _ = work => {
192 template = None;
193 },
194 new_template = devtools_next => {
195 template = Some(new_template);
196 },
197 hydration_data = rx_hydration => {
198 template = None;
199 #[cfg(feature = "hydrate")]
200 {
201 hydration_work = Some(hydration_data);
202 }
203 },
204 }
205 }
206
207 #[cfg(not(all(feature = "devtools", debug_assertions)))]
208 #[allow(unused)]
209 {
210 select! {
211 _ = work => {},
212 hyd = rx_hydration => {
213 #[cfg(feature = "hydrate")]
214 {
215 hydration_work = Some(hyd);
216 }
217 }
218 }
219 }
220 }
221
222 #[cfg(all(feature = "devtools", debug_assertions))]
223 if let Some(hr_msg) = template {
224 dioxus_devtools::apply_changes(&virtual_dom, &hr_msg);
226
227 if !hr_msg.assets.is_empty() {
228 crate::devtools::invalidate_browser_asset_cache();
229 }
230
231 if hr_msg.for_build_id == Some(dioxus_cli_config::build_id()) {
232 devtools::show_toast(
233 "Hot-patch success!",
234 &format!("App successfully patched in {} ms", hr_msg.ms_elapsed),
235 devtools::ToastLevel::Success,
236 std::time::Duration::from_millis(2000),
237 false,
238 );
239 }
240 }
241
242 #[cfg(feature = "hydrate")]
243 if let Some(hydration_data) = hydration_work {
244 websys_dom.rehydrate_streaming(hydration_data, &mut virtual_dom);
245 }
246
247 virtual_dom.render_immediate(&mut websys_dom);
260
261 websys_dom.flush_edits();
265 }
266}