dioxus_hot_reload/
lib.rs

1use std::{
2    io::{BufRead, BufReader},
3    path::PathBuf,
4};
5
6use dioxus_core::Template;
7#[cfg(feature = "file_watcher")]
8pub use dioxus_html::HtmlCtx;
9use interprocess::local_socket::LocalSocketStream;
10use serde::{Deserialize, Serialize};
11
12#[cfg(feature = "custom_file_watcher")]
13mod file_watcher;
14#[cfg(feature = "custom_file_watcher")]
15pub use file_watcher::*;
16
17/// A message the hot reloading server sends to the client
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(bound(deserialize = "'de: 'static"))]
20pub enum HotReloadMsg {
21    /// A template has been updated
22    UpdateTemplate(Template),
23
24    /// An asset discovered by rsx! has been updated
25    UpdateAsset(PathBuf),
26
27    /// The program needs to be recompiled, and the client should shut down
28    Shutdown,
29}
30
31/// Connect to the hot reloading listener. The callback provided will be called every time a template change is detected
32pub fn connect(callback: impl FnMut(HotReloadMsg) + Send + 'static) {
33    if cfg!(windows) {
34        connect_at(PathBuf::from("@dioxusin"), callback);
35    } else {
36        // FIXME: this is falling back onto the current directory when not running under cargo, which is how the CLI runs this.
37        // This needs to be fixed.
38        let _manifest_dir = std::env::var("CARGO_MANIFEST_DIR");
39
40        // get the cargo manifest directory, where the target dir lives
41        let mut path = match _manifest_dir {
42            Ok(manifest_dir) => PathBuf::from(manifest_dir),
43            Err(_) => std::env::current_dir().unwrap(),
44        };
45
46        // walk the path until we a find a socket named `dioxusin` inside that folder's target directory
47        loop {
48            let maybe = path.join("target").join("dioxusin");
49
50            if maybe.exists() {
51                path = maybe;
52                break;
53            }
54
55            // It's likely we're running under just cargo and not dx
56            path = match path.parent() {
57                Some(parent) => parent.to_path_buf(),
58                None => return,
59            };
60        }
61
62        println!("connecting to {:?}", path);
63        connect_at(path, callback);
64    }
65}
66
67pub fn connect_at(socket: PathBuf, mut callback: impl FnMut(HotReloadMsg) + Send + 'static) {
68    std::thread::spawn(move || {
69        // There might be a socket since the we're not running under the hot reloading server
70        let stream = if cfg!(windows) {
71            LocalSocketStream::connect("@dioxusin")
72        } else {
73            LocalSocketStream::connect(socket.clone())
74        };
75        let Ok(socket) = stream else {
76            println!(
77                "could not find hot reloading server at {:?}, make sure it's running",
78                socket
79            );
80            return;
81        };
82
83        let mut buf_reader = BufReader::new(socket);
84
85        loop {
86            let mut buf = String::new();
87
88            if let Err(err) = buf_reader.read_line(&mut buf) {
89                if err.kind() != std::io::ErrorKind::WouldBlock {
90                    break;
91                }
92            }
93
94            let Ok(template) = serde_json::from_str(Box::leak(buf.into_boxed_str())) else {
95                continue;
96            };
97
98            callback(template);
99        }
100    });
101}
102
103/// Start the hot reloading server with the current directory as the root
104#[macro_export]
105macro_rules! hot_reload_init {
106    () => {
107        #[cfg(debug_assertions)]
108        dioxus_hot_reload::init(dioxus_hot_reload::Config::new().root(env!("CARGO_MANIFEST_DIR")));
109    };
110
111    ($cfg: expr) => {
112        #[cfg(debug_assertions)]
113        dioxus_hot_reload::init($cfg.root(env!("CARGO_MANIFEST_DIR")));
114    };
115}