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;
24use crate::hydration::SuspenseMessage;
25use dioxus_core::VirtualDom;
26use dom::WebsysDom;
27use futures_util::{pin_mut, select, FutureExt, StreamExt};
28
29mod cfg;
30mod dom;
31
32mod events;
33pub mod launch;
34mod mutations;
35pub use events::*;
36
37#[cfg(feature = "document")]
38mod document;
39#[cfg(feature = "file_engine")]
40mod file_engine;
41#[cfg(feature = "document")]
42mod history;
43#[cfg(feature = "document")]
44pub use document::WebDocument;
45#[cfg(feature = "file_engine")]
46pub use file_engine::*;
47
48#[cfg(all(feature = "devtools", debug_assertions))]
49mod devtools;
50
51mod hydration;
52#[allow(unused)]
53pub use hydration::*;
54
55pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
66 #[cfg(feature = "document")]
67 virtual_dom.in_runtime(document::init_document);
68
69 let runtime = virtual_dom.runtime();
70
71 #[cfg(all(feature = "devtools", debug_assertions))]
72 let mut hotreload_rx = devtools::init(runtime.clone());
73
74 let should_hydrate = web_config.hydrate;
75
76 let mut websys_dom = WebsysDom::new(web_config, runtime);
77
78 let mut hydration_receiver: Option<futures_channel::mpsc::UnboundedReceiver<SuspenseMessage>> =
79 None;
80
81 if should_hydrate {
82 #[cfg(feature = "hydrate")]
83 {
84 websys_dom.skip_mutations = true;
85 #[wasm_bindgen::prelude::wasm_bindgen(inline_js = r#"
87 export function get_initial_hydration_data() {
88 const decoded = atob(window.initial_dioxus_hydration_data);
89 return Uint8Array.from(decoded, (c) => c.charCodeAt(0))
90 }
91 export function get_initial_hydration_debug_types() {
92 return window.initial_dioxus_hydration_debug_types;
93 }
94 export function get_initial_hydration_debug_locations() {
95 return window.initial_dioxus_hydration_debug_locations;
96 }
97 "#)]
98 extern "C" {
99 fn get_initial_hydration_data() -> js_sys::Uint8Array;
100 fn get_initial_hydration_debug_types() -> Option<Vec<String>>;
101 fn get_initial_hydration_debug_locations() -> Option<Vec<String>>;
102 }
103 let hydration_data = get_initial_hydration_data().to_vec();
104
105 #[cfg(debug_assertions)]
107 let debug_types = get_initial_hydration_debug_types();
108 #[cfg(not(debug_assertions))]
109 let debug_types = None;
110 #[cfg(debug_assertions)]
111 let debug_locations = get_initial_hydration_debug_locations();
112 #[cfg(not(debug_assertions))]
113 let debug_locations = None;
114
115 let server_data =
116 HTMLDataCursor::from_serialized(&hydration_data, debug_types, debug_locations);
117 if let Some(error) = server_data.error() {
119 virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error));
120 }
121 with_server_data(server_data, || {
122 virtual_dom.rebuild(&mut websys_dom);
123 });
124 websys_dom.skip_mutations = false;
125
126 let rx = websys_dom.rehydrate(&virtual_dom).unwrap();
127 hydration_receiver = Some(rx);
128
129 #[cfg(feature = "mounted")]
130 {
131 websys_dom.flush_queued_mounted_events();
133 }
134 }
135 #[cfg(not(feature = "hydrate"))]
136 {
137 panic!("Hydration is not enabled. Please enable the `hydrate` feature.");
138 }
139 } else {
140 virtual_dom.rebuild(&mut websys_dom);
141
142 websys_dom.flush_edits();
143 }
144
145 loop {
146 #[cfg(all(feature = "devtools", debug_assertions))]
149 let template;
150 #[allow(unused)]
151 let mut hydration_work: Option<SuspenseMessage> = None;
152
153 {
154 let work = virtual_dom.wait_for_work().fuse();
155 pin_mut!(work);
156
157 let mut hydration_receiver_iter = futures_util::stream::iter(&mut hydration_receiver)
158 .fuse()
159 .flatten();
160 let mut rx_hydration = hydration_receiver_iter.select_next_some();
161
162 #[cfg(all(feature = "devtools", debug_assertions))]
163 #[allow(unused)]
164 {
165 let mut devtools_next = hotreload_rx.select_next_some();
166 select! {
167 _ = work => {
168 template = None;
169 },
170 new_template = devtools_next => {
171 template = Some(new_template);
172 },
173 hydration_data = rx_hydration => {
174 template = None;
175 #[cfg(feature = "hydrate")]
176 {
177 hydration_work = Some(hydration_data);
178 }
179 },
180 }
181 }
182
183 #[cfg(not(all(feature = "devtools", debug_assertions)))]
184 #[allow(unused)]
185 {
186 select! {
187 _ = work => {},
188 hyd = rx_hydration => {
189 #[cfg(feature = "hydrate")]
190 {
191 hydration_work = Some(hyd);
192 }
193 }
194 }
195 }
196 }
197
198 #[cfg(all(feature = "devtools", debug_assertions))]
199 if let Some(hr_msg) = template {
200 dioxus_devtools::apply_changes(&virtual_dom, &hr_msg);
202
203 if !hr_msg.assets.is_empty() {
204 crate::devtools::invalidate_browser_asset_cache();
205 }
206 }
207
208 #[cfg(feature = "hydrate")]
209 if let Some(hydration_data) = hydration_work {
210 websys_dom.rehydrate_streaming(hydration_data, &mut virtual_dom);
211 }
212
213 virtual_dom.render_immediate(&mut websys_dom);
226
227 websys_dom.flush_edits();
231 }
232}