]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): ensure async setup error handling work with suspense during ssr
authorEvan You <yyx990803@gmail.com>
Fri, 26 Mar 2021 15:00:30 +0000 (11:00 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 26 Mar 2021 15:00:30 +0000 (11:00 -0400)
packages/runtime-core/src/component.ts
packages/server-renderer/__tests__/render.spec.ts
packages/server-renderer/__tests__/ssrSuspense.spec.ts
packages/server-renderer/src/render.ts

index fe81efedfaf2f3b060fcb4ac6f47fbdb4b297bea..1295b75936b2b33437de3f77aafed2f4d6d0a01e 100644 (file)
@@ -23,7 +23,7 @@ import {
 } from './componentProps'
 import { Slots, initSlots, InternalSlots } from './componentSlots'
 import { warn } from './warning'
-import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
+import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
 import { AppContext, createAppContext, AppConfig } from './apiCreateApp'
 import { Directive, validateDirectiveName } from './directives'
 import {
@@ -579,7 +579,7 @@ function setupStatefulComponent(
 
     currentInstance = instance
     pauseTracking()
-    const setupResult = callWithAsyncErrorHandling(
+    const setupResult = callWithErrorHandling(
       setup,
       instance,
       ErrorCodes.SETUP_FUNCTION,
@@ -591,9 +591,13 @@ function setupStatefulComponent(
     if (isPromise(setupResult)) {
       if (isSSR) {
         // return the promise so server-renderer can wait on it
-        return setupResult.then((resolvedResult: unknown) => {
-          handleSetupResult(instance, resolvedResult, isSSR)
-        })
+        return setupResult
+          .then((resolvedResult: unknown) => {
+            handleSetupResult(instance, resolvedResult, isSSR)
+          })
+          .catch(e => {
+            handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
+          })
       } else if (__FEATURE_SUSPENSE__) {
         // async setup returned Promise.
         // bail here and wait for re-entry.
index e537d9d78d506cc2f5bc1e15a38bb09de03b96f5..bb7009f8776c9f2ae12702036dfa1e935f03f472 100644 (file)
@@ -811,10 +811,8 @@ function testRender(type: string, render: typeof renderToString) {
 
       expect(fn2).toHaveBeenCalledTimes(1)
       expect(fn2).toBeCalledWith('async child error')
-
-      expect('Uncaught error in async setup').toHaveBeenWarned()
     })
-    
+
     // https://github.com/vuejs/vue-next/issues/3322
     test('effect onInvalidate does not error', async () => {
       const noop = () => {}
index 3cdd81373703e967ff3ed489eacbd9c0f6f914f5..4da4ce4b97dd72edcae39c4763de5900f0ed14e2 100644 (file)
@@ -29,6 +29,7 @@ describe('SSR Suspense', () => {
 
   test('reject', async () => {
     const Comp = {
+      errorCaptured: jest.fn(() => false),
       render() {
         return h(Suspense, null, {
           default: h(RejectingAsync),
@@ -38,10 +39,8 @@ describe('SSR Suspense', () => {
     }
 
     expect(await renderToString(createApp(Comp))).toBe(`<!---->`)
-    expect('Uncaught error in async setup').toHaveBeenWarned()
-    expect(
-      'Unhandled error during execution of setup function'
-    ).toHaveBeenWarned()
+
+    expect(Comp.errorCaptured).toHaveBeenCalledTimes(1)
     expect('missing template').toHaveBeenWarned()
   })
 
@@ -62,6 +61,7 @@ describe('SSR Suspense', () => {
 
   test('resolving component + rejecting component', async () => {
     const Comp = {
+      errorCaptured: jest.fn(() => false),
       render() {
         return h(Suspense, null, {
           default: h('div', [h(ResolvingAsync), h(RejectingAsync)]),
@@ -73,15 +73,14 @@ describe('SSR Suspense', () => {
     expect(await renderToString(createApp(Comp))).toBe(
       `<div><div>async</div><!----></div>`
     )
-    expect('Uncaught error in async setup').toHaveBeenWarned()
-    expect(
-      'Unhandled error during execution of setup function'
-    ).toHaveBeenWarned()
+
+    expect(Comp.errorCaptured).toHaveBeenCalledTimes(1)
     expect('missing template or render function').toHaveBeenWarned()
   })
 
   test('failing suspense in passing suspense', async () => {
     const Comp = {
+      errorCaptured: jest.fn(() => false),
       render() {
         return h(Suspense, null, {
           default: h('div', [
@@ -99,15 +98,14 @@ describe('SSR Suspense', () => {
     expect(await renderToString(createApp(Comp))).toBe(
       `<div><div>async</div><div><!----></div></div>`
     )
-    expect('Uncaught error in async setup').toHaveBeenWarned()
-    expect(
-      'Unhandled error during execution of setup function'
-    ).toHaveBeenWarned()
+
+    expect(Comp.errorCaptured).toHaveBeenCalledTimes(1)
     expect('missing template').toHaveBeenWarned()
   })
 
   test('passing suspense in failing suspense', async () => {
     const Comp = {
+      errorCaptured: jest.fn(() => false),
       render() {
         return h(Suspense, null, {
           default: h('div', [
@@ -125,10 +123,7 @@ describe('SSR Suspense', () => {
     expect(await renderToString(createApp(Comp))).toBe(
       `<div><!----><div><div>async</div></div></div>`
     )
-    expect('Uncaught error in async setup').toHaveBeenWarned()
-    expect(
-      'Unhandled error during execution of setup function'
-    ).toHaveBeenWarned()
+    expect(Comp.errorCaptured).toHaveBeenCalledTimes(1)
     expect('missing template').toHaveBeenWarned()
   })
 })
index c0ba83bea5b1e63574bb36bb7f24b3a42ae0fd75..77a268b516b91ade7e1547cba2cf2ba39ffee934 100644 (file)
@@ -89,11 +89,7 @@ export function renderComponentVNode(
   const hasAsyncSetup = isPromise(res)
   const prefetch = (vnode.type as ComponentOptions).serverPrefetch
   if (hasAsyncSetup || prefetch) {
-    let p = hasAsyncSetup
-      ? (res as Promise<void>).catch(err => {
-          warn(`[@vue/server-renderer]: Uncaught error in async setup:\n`, err)
-        })
-      : Promise.resolve()
+    let p = hasAsyncSetup ? (res as Promise<void>) : Promise.resolve()
     if (prefetch) {
       p = p.then(() => prefetch.call(instance.proxy)).catch(err => {
         warn(`[@vue/server-renderer]: Uncaught error in serverPrefetch:\n`, err)