Browse Source

fix(server-renderer): propagate sync errors from `ssrRenderSuspense` (#14804)

resolves nuxt/nuxt#28162
Daniel Roe 2 weeks ago
parent
commit
47609975e2

+ 25 - 1
packages/server-renderer/__tests__/ssrSuspense.spec.ts

@@ -1,5 +1,7 @@
-import { Suspense, createApp, h } from 'vue'
+import { Suspense, createApp, defineComponent, h } from 'vue'
 import { renderToString } from '../src/renderToString'
+import { ssrRenderComponent } from '../src/helpers/ssrRenderComponent'
+import { ssrRenderSuspense } from '../src/helpers/ssrRenderSuspense'
 
 describe('SSR Suspense', () => {
   const ResolvingAsync = {
@@ -103,6 +105,28 @@ describe('SSR Suspense', () => {
     expect('missing template').toHaveBeenWarned()
   })
 
+  // nuxt/nuxt#28162
+  test('propagates sync errors from compiled ssrRenderSuspense default slot', async () => {
+    const Throwing = defineComponent({
+      ssrRender(_ctx: any, _push: any) {
+        throw new TypeError('bang')
+      },
+    })
+
+    const Root = defineComponent({
+      ssrRender(_ctx: any, _push: any, _parent: any) {
+        ssrRenderSuspense(_push, {
+          default: () => {
+            _push(ssrRenderComponent(Throwing, null, null, _parent))
+          },
+          _: 1,
+        } as any)
+      },
+    })
+
+    await expect(renderToString(createApp(Root))).rejects.toThrow('bang')
+  })
+
   test('passing suspense in failing suspense', async () => {
     const Comp = {
       errorCaptured: vi.fn(() => false),

+ 5 - 2
packages/server-renderer/src/helpers/ssrRenderSuspense.ts

@@ -1,9 +1,12 @@
 import type { PushFn } from '../render'
 
-export async function ssrRenderSuspense(
+// Must remain synchronous: compiled output is `ssrRenderSuspense(_push, ...)`,
+// not `_push(ssrRenderSuspense(...))`, so any returned Promise (and its
+// rejection) would be silently discarded.
+export function ssrRenderSuspense(
   push: PushFn,
   { default: renderContent }: Record<string, (() => void) | undefined>,
-): Promise<void> {
+): void {
   if (renderContent) {
     renderContent()
   } else {