lib.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. use dioxus_core::internal::HotReloadedTemplate;
  2. use dioxus_core::{ScopeId, VirtualDom};
  3. use dioxus_signals::{GlobalKey, Signal, Writable};
  4. pub use dioxus_devtools_types::*;
  5. pub use subsecond;
  6. use subsecond::PatchError;
  7. /// Applies template and literal changes to the VirtualDom
  8. ///
  9. /// Assets need to be handled by the renderer.
  10. pub fn apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) {
  11. try_apply_changes(dom, msg).unwrap()
  12. }
  13. /// Applies template and literal changes to the VirtualDom, but doesn't panic if patching fails.
  14. ///
  15. /// Assets need to be handled by the renderer.
  16. pub fn try_apply_changes(dom: &VirtualDom, msg: &HotReloadMsg) -> Result<(), PatchError> {
  17. dom.runtime().on_scope(ScopeId::ROOT, || {
  18. // 1. Update signals...
  19. let ctx = dioxus_signals::get_global_context();
  20. for template in &msg.templates {
  21. let value = template.template.clone();
  22. let key = GlobalKey::File {
  23. file: template.key.file.as_str(),
  24. line: template.key.line as _,
  25. column: template.key.column as _,
  26. index: template.key.index as _,
  27. };
  28. if let Some(mut signal) = ctx.get_signal_with_key(key.clone()) {
  29. signal.set(Some(value));
  30. }
  31. }
  32. // 2. Attempt to hotpatch
  33. if let Some(jump_table) = msg.jump_table.as_ref().cloned() {
  34. if msg.for_build_id == Some(dioxus_cli_config::build_id()) {
  35. let our_pid = if cfg!(target_family = "wasm") {
  36. None
  37. } else {
  38. Some(std::process::id())
  39. };
  40. if msg.for_pid == our_pid {
  41. unsafe { subsecond::apply_patch(jump_table) }?;
  42. dioxus_core::prelude::force_all_dirty();
  43. ctx.clear::<Signal<Option<HotReloadedTemplate>>>();
  44. }
  45. }
  46. }
  47. Ok(())
  48. })
  49. }
  50. /// Connect to the devserver and handle its messages with a callback.
  51. ///
  52. /// This doesn't use any form of security or protocol, so it's not safe to expose to the internet.
  53. #[cfg(not(target_family = "wasm"))]
  54. pub fn connect(callback: impl FnMut(DevserverMsg) + Send + 'static) {
  55. let Some(endpoint) = dioxus_cli_config::devserver_ws_endpoint() else {
  56. return;
  57. };
  58. connect_at(endpoint, callback);
  59. }
  60. /// Connect to the devserver and handle hot-patch messages only, implementing the subsecond hotpatch
  61. /// protocol.
  62. ///
  63. /// This is intended to be used by non-dioxus projects that want to use hotpatching.
  64. ///
  65. /// To handle the full devserver protocol, use `connect` instead.
  66. #[cfg(not(target_family = "wasm"))]
  67. pub fn connect_subsecond() {
  68. connect(|msg| {
  69. if let DevserverMsg::HotReload(hot_reload_msg) = msg {
  70. if let Some(jumptable) = hot_reload_msg.jump_table {
  71. if hot_reload_msg.for_pid == Some(std::process::id()) {
  72. unsafe { subsecond::apply_patch(jumptable).unwrap() };
  73. }
  74. }
  75. }
  76. });
  77. }
  78. #[cfg(not(target_family = "wasm"))]
  79. pub fn connect_at(endpoint: String, mut callback: impl FnMut(DevserverMsg) + Send + 'static) {
  80. std::thread::spawn(move || {
  81. let uri = format!(
  82. "{endpoint}?aslr_reference={}&build_id={}&pid={}",
  83. subsecond::aslr_reference(),
  84. dioxus_cli_config::build_id(),
  85. std::process::id()
  86. );
  87. let (mut websocket, _req) = match tungstenite::connect(uri) {
  88. Ok((websocket, req)) => (websocket, req),
  89. Err(_) => return,
  90. };
  91. while let Ok(msg) = websocket.read() {
  92. if let tungstenite::Message::Text(text) = msg {
  93. if let Ok(msg) = serde_json::from_str(&text) {
  94. callback(msg);
  95. }
  96. }
  97. }
  98. });
  99. }