Fix the root error boundary during suspense in ssr (#4710)
* Fix the root error boundary during suspense in ssr * fix some typos
This commit is contained in:
1 parent
10912f4750
commit
ad40f81607
9 files changed
+49
-13
No files matched your search
@@ -2754,7 +2754,7 @@ impl BuildRequest {
|
||||
),
|
||||
("OPENSSL_LIBS".to_string(), openssl_libs.into()),
|
||||
// Set the wry env vars - this is where wry will dump its kotlin files.
|
||||
// Their setup is really annyoing and requires us to hardcode `dx` to specific versions of tao/wry.
|
||||
// Their setup is really annoying and requires us to hardcode `dx` to specific versions of tao/wry.
|
||||
(
|
||||
"WRY_ANDROID_PACKAGE".to_string(),
|
||||
"dev.dioxus.main".to_string().into(),
|
||||
@@ -3926,7 +3926,7 @@ __wbg_init({{module_or_path: "/{}/{wasm_path}"}}).then((wasm) => {{
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path to the wasm-bindgen output files. Either the direct file or the opitmized one depending on the build mode
|
||||
/// Get the path to the wasm-bindgen output files. Either the direct file or the optimized one depending on the build mode
|
||||
fn bundled_wasm_path(&self, assets: &AssetManifest) -> String {
|
||||
let wasm_bindgen_wasm_out = self.wasm_bindgen_wasm_output_file();
|
||||
if self.should_bundle_to_asset() {
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::borrow::Cow;
|
||||
|
||||
/// Take this type and format it into a Cow<'static, str>
|
||||
///
|
||||
/// This trait exists so libraries like manganis can implement this type for asssets without depending
|
||||
/// This trait exists so libraries like manganis can implement this type for assets without depending
|
||||
/// on dioxus-core, which can be heavy in proc macros and build scripts.
|
||||
///
|
||||
/// We don't want a blanket impl for T: Display because that might conflict for the other integral data
|
||||
|
||||
@@ -795,7 +795,7 @@ impl VNode {
|
||||
fn write_attrs(
|
||||
&self,
|
||||
mount: MountId,
|
||||
dynamic_attrbiutes_iter: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
|
||||
dynamic_attributes_iter: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
|
||||
root_idx: u8,
|
||||
dom: &mut VirtualDom,
|
||||
to: &mut impl WriteMutations,
|
||||
@@ -804,7 +804,7 @@ impl VNode {
|
||||
// Only take nodes that are under this root node
|
||||
let from_root_node = |(_, path): &(usize, &[u8])| path.first() == Some(&root_idx);
|
||||
while let Some((attribute_idx, attribute_path)) =
|
||||
dynamic_attrbiutes_iter.next_if(from_root_node)
|
||||
dynamic_attributes_iter.next_if(from_root_node)
|
||||
{
|
||||
let attribute = &self.dynamic_attrs[attribute_idx];
|
||||
|
||||
|
||||
@@ -45,11 +45,17 @@ impl ScopeId {
|
||||
/// let my_persistent_state = Signal::new_in_scope(String::new(), ScopeId::APP);
|
||||
/// ```
|
||||
// ScopeId(0) is the root scope wrapper
|
||||
// ScopeId(1) is the default error boundary
|
||||
// ScopeId(2) is the default suspense boundary
|
||||
// ScopeId(1) is the default suspense boundary
|
||||
// ScopeId(2) is the default error boundary
|
||||
// ScopeId(3) is the users root scope
|
||||
pub const APP: ScopeId = ScopeId(3);
|
||||
|
||||
/// The ScopeId of the topmost error boundary in the tree.
|
||||
pub const ROOT_ERROR_BOUNDARY: ScopeId = ScopeId(2);
|
||||
|
||||
/// The ScopeId of the topmost suspense boundary in the tree.
|
||||
pub const ROOT_SUSPENSE_BOUNDARY: ScopeId = ScopeId(1);
|
||||
|
||||
/// The ScopeId of the topmost scope in the tree.
|
||||
/// This will be higher up in the tree than [`ScopeId::APP`] because dioxus inserts a default [`crate::SuspenseBoundary`] and [`crate::ErrorBoundary`] at the root of the tree.
|
||||
// ScopeId(0) is the root scope wrapper
|
||||
|
||||
@@ -42,6 +42,19 @@ test("error route", async ({ request }) => {
|
||||
expect(response.status()).toBe(500);
|
||||
});
|
||||
|
||||
// The async error route should return 500
|
||||
test("async error route", async ({ request }) => {
|
||||
await waitForBuild(request);
|
||||
const response = await request.get("http://localhost:8888/async-error");
|
||||
|
||||
expect(response.status()).toBe(500);
|
||||
|
||||
// Expect the response to contain the error message
|
||||
const errorMessage = "Async error from a server function";
|
||||
const text = await response.text();
|
||||
expect(text).toContain(errorMessage);
|
||||
});
|
||||
|
||||
// An unknown route should return 404
|
||||
test("unknown route", async ({ request }) => {
|
||||
await waitForBuild(request);
|
||||
|
||||
@@ -30,6 +30,9 @@ enum Route {
|
||||
#[route("/error")]
|
||||
ThrowsError,
|
||||
|
||||
#[route("/async-error")]
|
||||
ThrowsAsyncError,
|
||||
|
||||
#[route("/can-go-back")]
|
||||
HydrateCanGoBack,
|
||||
}
|
||||
@@ -52,6 +55,20 @@ fn ThrowsError() -> Element {
|
||||
)));
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn ThrowsAsyncError() -> Element {
|
||||
#[server]
|
||||
async fn error_after_delay() -> ServerFnResult<()> {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
Err(ServerFnError::new("Async error from a server function"))
|
||||
}
|
||||
|
||||
use_server_future(error_after_delay)?().unwrap()?;
|
||||
rsx! {
|
||||
"Hello, world!"
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Home() -> Element {
|
||||
let route: Route = use_route();
|
||||
|
||||
@@ -241,7 +241,7 @@ impl SsrRendererPool {
|
||||
|
||||
// check if there are any errors
|
||||
let errors = virtual_dom.in_runtime(|| {
|
||||
let error_context: ErrorContext = ScopeId::APP
|
||||
let error_context: ErrorContext = ScopeId::ROOT_ERROR_BOUNDARY
|
||||
.consume_context()
|
||||
.expect("The root should be under an error boundary");
|
||||
let errors = error_context.errors();
|
||||
|
||||
@@ -499,7 +499,7 @@ pub unsafe fn apply_patch(mut table: JumpTable) -> Result<(), PatchError> {
|
||||
// On non-wasm platforms we can just use libloading and the known aslr offsets to load the library
|
||||
#[cfg(any(unix, windows))]
|
||||
{
|
||||
// on android we try to cirumvent permissions issues by copying the library to a memmap and then libloading that
|
||||
// on android we try to circumvent permissions issues by copying the library to a memmap and then libloading that
|
||||
#[cfg(target_os = "android")]
|
||||
let lib = Box::leak(Box::new(android_memmap_dlopen(&table.lib)?));
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ impl<'a> Splitter<'a> {
|
||||
self.clear_data_segments(&mut out, &unique_symbols);
|
||||
|
||||
// Clear out the element segments and then add in the initializers for the shared imports
|
||||
self.create_ifunc_initialzers(&mut out, &unique_symbols);
|
||||
self.create_ifunc_initializers(&mut out, &unique_symbols);
|
||||
|
||||
// Convert our split module's functions to real functions that call the indirect function
|
||||
self.add_split_imports(
|
||||
@@ -332,7 +332,7 @@ impl<'a> Splitter<'a> {
|
||||
self.clear_data_segments(&mut out, &unique_symbols);
|
||||
|
||||
// Clear out the element segments and then add in the initializers for the shared imports
|
||||
self.create_ifunc_initialzers(&mut out, &unique_symbols);
|
||||
self.create_ifunc_initializers(&mut out, &unique_symbols);
|
||||
|
||||
// We have to make sure our table matches that of the other tables even though we don't call them.
|
||||
let ifunc_table_id = self.load_funcref_table(&mut out);
|
||||
@@ -554,7 +554,7 @@ impl<'a> Splitter<'a> {
|
||||
}
|
||||
|
||||
/// Creates the jump points
|
||||
fn create_ifunc_initialzers(&self, out: &mut Module, unique_symbols: &HashSet<Node>) {
|
||||
fn create_ifunc_initializers(&self, out: &mut Module, unique_symbols: &HashSet<Node>) {
|
||||
let ifunc_table = self.load_funcref_table(out);
|
||||
|
||||
let mut initializers = HashMap::new();
|
||||
@@ -1009,7 +1009,7 @@ impl<'a> Splitter<'a> {
|
||||
let mut recovered_children = HashSet::new();
|
||||
for lost in lost_children {
|
||||
match lost {
|
||||
// Functions need to be found - the wasm decsribe functions are usually completely dissolved
|
||||
// Functions need to be found - the wasm describe functions are usually completely dissolved
|
||||
Node::Function(id) => {
|
||||
let func = original.module.funcs.get(id);
|
||||
let name = func.name.as_ref().unwrap();
|
||||
|
||||
Reference in new issue
Block a user