Skip to content

Commit

Permalink
fix semver breakage in the wasm_bindgen export
Browse files Browse the repository at this point in the history
  • Loading branch information
jkelleyrtp committed Jan 8, 2025
1 parent bac301f commit 95f1922
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 65 deletions.
3 changes: 1 addition & 2 deletions packages/fullstack/src/serve_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,9 @@ pub enum StreamingMode {
pub struct ServeConfig {
pub(crate) index: IndexHtml,
pub(crate) incremental: Option<dioxus_isrg::IncrementalRendererConfig>,
pub(crate) streaming_mode: StreamingMode,

#[allow(unused)]
pub(crate) context_providers: ContextProviders,
pub(crate) streaming_mode: StreamingMode,
}

impl LaunchConfig for ServeConfig {}
Expand Down
2 changes: 1 addition & 1 deletion packages/interpreter/src/js/hash.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[6449103750905854967, 17669692872757955279, 13069001215487072322, 7552882699631236642, 4823736819599448666, 5444526391971481782, 12156139214887111728, 5052021921702764563, 12925655762638175824, 5638004933879392817]
[6449103750905854967, 17669692872757955279, 13069001215487072322, 3463239101742491625, 10304266762117349606, 5444526391971481782, 12156139214887111728, 5052021921702764563, 12925655762638175824, 5638004933879392817]
2 changes: 1 addition & 1 deletion packages/interpreter/src/js/hydrate.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions packages/interpreter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub use unified_bindings::*;
// Common bindings for minimal usage.
#[cfg(all(feature = "minimal_bindings", feature = "webonly"))]
pub mod minimal_bindings {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsValue};

/// Some useful snippets that we use to share common functionality between the different platforms we support.
///
Expand All @@ -45,7 +45,18 @@ pub mod minimal_bindings {
extern "C" {
/// Register a callback that that will be called to hydrate a node at the given id with data from the server
pub fn register_rehydrate_chunk_for_streaming(
closure: &wasm_bindgen::closure::Closure<
closure: &Closure<dyn FnMut(Vec<u32>, js_sys::Uint8Array)>,
);

/// Register a callback that that will be called to hydrate a node at the given id with data from the server
/// Contains additional attributes that are used to hydrate the node
///
/// # Compatibility Note
///
/// We maintain two versions of this function for backwards compatibility. Both defer to the
/// same underlying implementation. The additional arguments are the debug locations.
pub fn register_rehydrate_chunk_for_streaming_debug(
closure: &Closure<
dyn FnMut(Vec<u32>, js_sys::Uint8Array, Option<Vec<String>>, Option<Vec<String>>),
>,
);
Expand Down
9 changes: 9 additions & 0 deletions packages/interpreter/src/ts/hydrate.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import "./hydrate_types";
import { HydrationCallback } from "./hydrate_types";

// We changed the signature to support debug types and locations but need to keep the old signature
// for backwards compatibility.
export function register_rehydrate_chunk_for_streaming(
callback: (id: number[], data: Uint8Array) => void
): void {
register_rehydrate_chunk_for_streaming_debug(callback)
}

export function register_rehydrate_chunk_for_streaming_debug(
callback: HydrationCallback
): void {
window.hydration_callback = callback;
for (let i = 0; i < window.hydrate_queue.length; i++) {
Expand Down
18 changes: 10 additions & 8 deletions packages/interpreter/src/ts/hydrate_types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
export {};
export { HydrationCallback };

type HydrationCallback = (
id: number[],
data: Uint8Array,
debug_types: string[] | null,
debug_locations: string[] | null
) => void;

declare global {
interface Window {
hydrate_queue: [number[], Uint8Array, string[] | null, string[] | null][];
hydration_callback:
| null
| ((
id: number[],
data: Uint8Array,
debug_types: string[] | null,
debug_locations: string[] | null
) => void);
| null
| HydrationCallback;
}
}
84 changes: 48 additions & 36 deletions packages/web/src/hydration/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,12 @@ impl HTMLDataCursor {
};

// The first item is always an error if it exists
let error = myself
myself.error = myself
.take::<Option<CapturedError>>()
.ok()
.flatten()
.flatten();

myself.error = error;

myself
}

Expand All @@ -101,42 +99,56 @@ impl HTMLDataCursor {
);
return Err(TakeDataError::DataNotAvailable);
}

let bytes = self.data[current].as_ref();

// Make sure we increment the index regardless of how the deserialization goes
self.index.set(current + 1);
match bytes {
Some(bytes) => match ciborium::from_reader(Cursor::new(bytes)) {
Ok(x) => Ok(Some(x)),
Err(err) => {
#[cfg(debug_assertions)]
{
let debug_type = self
.debug_types
.as_ref()
.and_then(|types| types.get(current));
let debug_locations = self
.debug_locations
.as_ref()
.and_then(|locations| locations.get(current));

if let (Some(debug_type), Some(debug_locations)) =
(debug_type, debug_locations)
{
let client_type = std::any::type_name::<T>();
let client_location = std::panic::Location::caller();
// We we have debug types and a location, we can provide a more helpful error message
tracing::error!(
"Error deserializing data: {err:?}\n\nThis type was serialized on the server at {debug_locations} with the type name {debug_type}. The client failed to deserialize the type {client_type} at {client_location}.",
);
return Err(TakeDataError::DeserializationError(err));
}
}
// Otherwise, just log the generic deserialization error
tracing::error!("Error deserializing data: {:?}", err);
Err(TakeDataError::DeserializationError(err))
}
},
None => Ok(None),

// If there is no data, return None
let Some(bytes) = bytes else {
return Ok(None);
};

ciborium::from_reader(Cursor::new(bytes))
.map_err(|err| self.make_detailed_deserialize_error::<T>(current, err))
}

fn make_detailed_deserialize_error<T: DeserializeOwned>(
&self,
current: usize,
err: ciborium::de::Error<std::io::Error>,
) -> TakeDataError {
// Try to provide a *very* helpful error message if we have debug types and locations
#[cfg(debug_assertions)]
{
let debug_type = self
.debug_types
.as_ref()
.and_then(|types| types.get(current));

let debug_locations = self
.debug_locations
.as_ref()
.and_then(|locations| locations.get(current));

if let (Some(debug_type), Some(debug_locations)) = (debug_type, debug_locations) {
let client_type = std::any::type_name::<T>();
let client_location = std::panic::Location::caller();
// We we have debug types and a location, we can provide a more helpful error message
// todo(jon): wire this up to devtools with a json emit
tracing::error!(
r#"Error deserializing data: {err:?}
This type was serialized on the server at {debug_locations} with the type name {debug_type}.
The client failed to deserialize the type {client_type} at {client_location}."#,
);
return TakeDataError::DeserializationError(err);
}
}

// Otherwise, in release, just log the generic deserialization error
tracing::error!("Error deserializing data: {:?}", err);
TakeDataError::DeserializationError(err)
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/web/src/hydration/hydrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ impl WebsysDom {
});
};
let closure = wasm_bindgen::closure::Closure::new(closure);
dioxus_interpreter_js::minimal_bindings::register_rehydrate_chunk_for_streaming(&closure);
dioxus_interpreter_js::minimal_bindings::register_rehydrate_chunk_for_streaming_debug(
&closure,
);
closure.forget();

// Rehydrate the root scope that was rendered on the server. We will likely run into suspense boundaries.
Expand Down
28 changes: 14 additions & 14 deletions packages/web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,20 @@ pub async fn run(mut virtual_dom: VirtualDom, web_config: Config) -> ! {
fn get_initial_hydration_debug_types() -> Option<Vec<String>>;
fn get_initial_hydration_debug_locations() -> Option<Vec<String>>;
}
let hydration_data = get_initial_hydration_data().to_vec();

// If we are running in debug mode, also get the debug types and locations
#[cfg(debug_assertions)]
let debug_types = get_initial_hydration_debug_types();
#[cfg(not(debug_assertions))]
let debug_types = None;
#[cfg(debug_assertions)]
let debug_locations = get_initial_hydration_debug_locations();
#[cfg(not(debug_assertions))]
let debug_locations = None;

let server_data =
HTMLDataCursor::from_serialized(&hydration_data, debug_types, debug_locations);

let server_data = HTMLDataCursor::from_serialized(
&get_initial_hydration_data().to_vec(),
// If we are running in debug mode, also get the debug types and locations
match cfg!(debug_assertions) {
true => get_initial_hydration_debug_types(),
false => None,
},
match cfg!(debug_assertions) {
true => get_initial_hydration_debug_locations(),
false => None,
},
);

// If the server serialized an error into the root suspense boundary, throw it into the root scope
if let Some(error) = server_data.error() {
virtual_dom.in_runtime(|| dioxus_core::ScopeId::APP.throw_error(error));
Expand Down

0 comments on commit 95f1922

Please sign in to comment.