From a5045ac01c94b66f3c89a50c39bc1f23917d89ea Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 27 Oct 2022 11:18:52 +0200 Subject: [PATCH 1/5] fix(nuxt): pass async-data errors through to client --- .../nuxt/src/app/composables/asyncData.ts | 32 ++++++++++++++----- packages/nuxt/src/app/composables/fetch.ts | 4 +-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 24acd18f5bd..acc2a2ba058 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -1,6 +1,7 @@ import { onBeforeMount, onServerPrefetch, onUnmounted, ref, getCurrentInstance, watch, unref } from 'vue' import type { Ref, WatchSource } from 'vue' import { NuxtApp, useNuxtApp } from '../nuxt' +import { createError } from './error' export type _Transform = (input: Input) => Output @@ -61,7 +62,7 @@ export function useAsyncData< > ( handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData, PickKeys>, DataE | null | true> +): AsyncData, PickKeys>, DataE | null> export function useAsyncData< DataT, DataE = Error, @@ -71,13 +72,13 @@ export function useAsyncData< key: string, handler: (ctx?: NuxtApp) => Promise, options?: AsyncDataOptions -): AsyncData, PickKeys>, DataE | null | true> +): AsyncData, PickKeys>, DataE | null> export function useAsyncData< DataT, DataE = Error, Transform extends _Transform = _Transform, PickKeys extends KeyOfRes = KeyOfRes -> (...args: any[]): AsyncData, PickKeys>, DataE | null | true> { +> (...args: any[]): AsyncData, PickKeys>, DataE | null> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } @@ -114,7 +115,7 @@ export function useAsyncData< nuxt._asyncData[key] = { data: ref(useInitialCache() ? nuxt.payload.data[key] : options.default?.() ?? null), pending: ref(!useInitialCache()), - error: ref(nuxt.payload._errors[key] ?? null) + error: ref(nuxt.payload._errors[key] ? createError(nuxt.payload._errors[key]) : null) } } // TODO: Else, Soemhow check for confliciting keys with different defaults or fetcher @@ -168,7 +169,22 @@ export function useAsyncData< asyncData.pending.value = false nuxt.payload.data[key] = asyncData.data.value if (asyncData.error.value) { - nuxt.payload._errors[key] = true + // Use `createError` to normalize the error + const _err = createError(asyncData.error.value) + const err = { ..._err } + // Strip as many keys out as possible to reduce payload size + err.message = _err.message + delete err.stack + // @ts-expect-error + delete err.__nuxt_error + // @ts-expect-error + delete err.fatal + // @ts-expect-error + delete err.unhandled + if (!Object.keys(err.data).length) { + delete err.data + } + nuxt.payload._errors[key] = err } delete nuxt._asyncDataPromises[key] }) @@ -240,7 +256,7 @@ export function useLazyAsyncData< > ( handler: (ctx?: NuxtApp) => Promise, options?: Omit, 'lazy'> -): AsyncData, PickKeys>, DataE | null | true> +): AsyncData, PickKeys>, DataE | null> export function useLazyAsyncData< DataT, DataE = Error, @@ -250,13 +266,13 @@ export function useLazyAsyncData< key: string, handler: (ctx?: NuxtApp) => Promise, options?: Omit, 'lazy'> -): AsyncData, PickKeys>, DataE | null | true> +): AsyncData, PickKeys>, DataE | null> export function useLazyAsyncData< DataT, DataE = Error, Transform extends _Transform = _Transform, PickKeys extends KeyOfRes = KeyOfRes -> (...args: any[]): AsyncData, PickKeys>, DataE | null | true> { +> (...args: any[]): AsyncData, PickKeys>, DataE | null> { const autoKey = typeof args[args.length - 1] === 'string' ? args.pop() : undefined if (typeof args[0] !== 'string') { args.unshift(autoKey) } const [key, handler, options] = args as [string, (ctx?: NuxtApp) => Promise, AsyncDataOptions] diff --git a/packages/nuxt/src/app/composables/fetch.ts b/packages/nuxt/src/app/composables/fetch.ts index bf23534ca7c..d55d7e98dce 100644 --- a/packages/nuxt/src/app/composables/fetch.ts +++ b/packages/nuxt/src/app/composables/fetch.ts @@ -30,7 +30,7 @@ export function useFetch< > ( request: Ref | ReqT | (() => ReqT), opts?: UseFetchOptions<_ResT, Transform, PickKeys> -): AsyncData, PickKeys>, ErrorT | null | true> +): AsyncData, PickKeys>, ErrorT | null> export function useFetch< ResT = void, ErrorT = FetchError, @@ -114,7 +114,7 @@ export function useLazyFetch< > ( request: Ref | ReqT | (() => ReqT), opts?: Omit, 'lazy'> -): AsyncData, PickKeys>, ErrorT | null | true> +): AsyncData, PickKeys>, ErrorT | null> export function useLazyFetch< ResT = void, ErrorT = FetchError, From 8e3a9c1fe8e9917b79cb41c8034321852ae902cf Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 27 Oct 2022 11:24:59 +0200 Subject: [PATCH 2/5] fix: handle undefined `.data` property --- packages/nuxt/src/app/composables/asyncData.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index acc2a2ba058..e5cf1513926 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -181,7 +181,7 @@ export function useAsyncData< delete err.fatal // @ts-expect-error delete err.unhandled - if (!Object.keys(err.data).length) { + if (err.data && !Object.keys(err.data).length) { delete err.data } nuxt.payload._errors[key] = err From 1e240125faccf29492f156fd643e6fae45804239 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 27 Oct 2022 11:35:29 +0200 Subject: [PATCH 3/5] test: update type fixture --- test/fixtures/basic/types.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts index 0fc4e363719..0af36e506a8 100644 --- a/test/fixtures/basic/types.ts +++ b/test/fixtures/basic/types.ts @@ -28,8 +28,8 @@ describe('API routes', () => { expectTypeOf(useAsyncData('api-other', () => $fetch('/api/other')).data).toEqualTypeOf>() expectTypeOf(useAsyncData('api-generics', () => $fetch('/test')).data).toEqualTypeOf>() - expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() - expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useAsyncData('api-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() expectTypeOf(useLazyAsyncData('lazy-api-hello', () => $fetch('/api/hello')).data).toEqualTypeOf>() expectTypeOf(useLazyAsyncData('lazy-api-hey', () => $fetch('/api/hey')).data).toEqualTypeOf>() @@ -37,8 +37,8 @@ describe('API routes', () => { expectTypeOf(useLazyAsyncData('lazy-api-other', () => $fetch('/api/other')).data).toEqualTypeOf>() expectTypeOf(useLazyAsyncData('lazy-api-generics', () => $fetch('/test')).data).toEqualTypeOf>() - expectTypeOf(useLazyAsyncData('lazy-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() - expectTypeOf(useLazyAsyncData('lazy-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useLazyAsyncData('lazy-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() + expectTypeOf(useLazyAsyncData('lazy-error-generics', () => $fetch('/error')).error).toEqualTypeOf>() }) it('works with useFetch', () => { @@ -48,8 +48,8 @@ describe('API routes', () => { expectTypeOf(useFetch('/api/other').data).toEqualTypeOf>() expectTypeOf(useFetch('/test').data).toEqualTypeOf>() - expectTypeOf(useFetch('/error').error).toEqualTypeOf>() - expectTypeOf(useFetch('/error').error).toEqualTypeOf>() + expectTypeOf(useFetch('/error').error).toEqualTypeOf>() + expectTypeOf(useFetch('/error').error).toEqualTypeOf>() expectTypeOf(useLazyFetch('/api/hello').data).toEqualTypeOf>() expectTypeOf(useLazyFetch('/api/hey').data).toEqualTypeOf>() From e5f75062aabe9cecb29d77b030fe7b324a88b1f0 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 27 Oct 2022 11:50:03 +0200 Subject: [PATCH 4/5] test: for goodness sake --- test/fixtures/basic/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts index 0af36e506a8..40b90b012e4 100644 --- a/test/fixtures/basic/types.ts +++ b/test/fixtures/basic/types.ts @@ -58,8 +58,8 @@ describe('API routes', () => { expectTypeOf(useLazyFetch('/api/other').data).toEqualTypeOf>() expectTypeOf(useLazyFetch('/test').data).toEqualTypeOf>() - expectTypeOf(useLazyFetch('/error').error).toEqualTypeOf>() - expectTypeOf(useLazyFetch('/error').error).toEqualTypeOf>() + expectTypeOf(useLazyFetch('/error').error).toEqualTypeOf>() + expectTypeOf(useLazyFetch('/error').error).toEqualTypeOf>() }) }) From 79f21bb30c52411344dbc1c610315c61757f8f95 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 27 Oct 2022 15:35:03 +0200 Subject: [PATCH 5/5] feat: use built-in `.toJSON()` --- packages/nuxi/package.json | 2 +- packages/nuxt/package.json | 2 +- .../nuxt/src/app/composables/asyncData.ts | 18 ++--------------- packages/vite/package.json | 2 +- pnpm-lock.yaml | 20 +++++++++---------- 5 files changed, 15 insertions(+), 29 deletions(-) diff --git a/packages/nuxi/package.json b/packages/nuxi/package.json index 64bd6147f93..c6c71d2741e 100644 --- a/packages/nuxi/package.json +++ b/packages/nuxi/package.json @@ -35,7 +35,7 @@ "execa": "^6.1.0", "flat": "^5.0.2", "giget": "^0.1.7", - "h3": "^0.8.5", + "h3": "^0.8.6", "jiti": "^1.16.0", "listhen": "^0.3.4", "mlly": "^0.5.16", diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 04d5f7b22eb..e14c533d648 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -52,7 +52,7 @@ "escape-string-regexp": "^5.0.0", "fs-extra": "^10.1.0", "globby": "^13.1.2", - "h3": "^0.8.5", + "h3": "^0.8.6", "hash-sum": "^2.0.0", "hookable": "^5.4.1", "knitwork": "^0.1.2", diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index e5cf1513926..2a1446dd15a 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -169,22 +169,8 @@ export function useAsyncData< asyncData.pending.value = false nuxt.payload.data[key] = asyncData.data.value if (asyncData.error.value) { - // Use `createError` to normalize the error - const _err = createError(asyncData.error.value) - const err = { ..._err } - // Strip as many keys out as possible to reduce payload size - err.message = _err.message - delete err.stack - // @ts-expect-error - delete err.__nuxt_error - // @ts-expect-error - delete err.fatal - // @ts-expect-error - delete err.unhandled - if (err.data && !Object.keys(err.data).length) { - delete err.data - } - nuxt.payload._errors[key] = err + // We use `createError` and its .toJSON() property to normalize the error + nuxt.payload._errors[key] = createError(asyncData.error.value) } delete nuxt._asyncDataPromises[key] }) diff --git a/packages/vite/package.json b/packages/vite/package.json index cc2e44901ab..d81ee527663 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -33,7 +33,7 @@ "externality": "^0.2.2", "fs-extra": "^10.1.0", "get-port-please": "^2.6.1", - "h3": "^0.8.5", + "h3": "^0.8.6", "knitwork": "^0.1.2", "magic-string": "^0.26.7", "mlly": "^0.5.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21f301b607f..6256b102015 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -364,7 +364,7 @@ importers: flat: ^5.0.2 fsevents: ~2.3.2 giget: ^0.1.7 - h3: ^0.8.5 + h3: ^0.8.6 jiti: ^1.16.0 listhen: ^0.3.4 mlly: ^0.5.16 @@ -395,7 +395,7 @@ importers: execa: 6.1.0 flat: 5.0.2 giget: 0.1.7 - h3: 0.8.5 + h3: 0.8.6 jiti: 1.16.0 listhen: 0.3.4 mlly: 0.5.16 @@ -427,7 +427,7 @@ importers: escape-string-regexp: ^5.0.0 fs-extra: ^10.1.0 globby: ^13.1.2 - h3: ^0.8.5 + h3: ^0.8.6 hash-sum: ^2.0.0 hookable: ^5.4.1 knitwork: ^0.1.2 @@ -471,7 +471,7 @@ importers: escape-string-regexp: 5.0.0 fs-extra: 10.1.0 globby: 13.1.2 - h3: 0.8.5 + h3: 0.8.6 hash-sum: 2.0.0 hookable: 5.4.1 knitwork: 0.1.2 @@ -590,7 +590,7 @@ importers: externality: ^0.2.2 fs-extra: ^10.1.0 get-port-please: ^2.6.1 - h3: ^0.8.5 + h3: ^0.8.6 knitwork: ^0.1.2 magic-string: ^0.26.7 mlly: ^0.5.16 @@ -626,7 +626,7 @@ importers: externality: 0.2.2 fs-extra: 10.1.0 get-port-please: 2.6.1 - h3: 0.8.5 + h3: 0.8.6 knitwork: 0.1.2 magic-string: 0.26.7 mlly: 0.5.16 @@ -5436,8 +5436,8 @@ packages: dependencies: duplexer: 0.1.2 - /h3/0.8.5: - resolution: {integrity: sha512-A+rVzJ+31e67JJzlRf2Ycphu/mvl2qknbpch38xRfrs9HuGSKTtOWuzPnpgaEGIfnzuD/BsDOfhQLJevXEm3ag==} + /h3/0.8.6: + resolution: {integrity: sha512-CSWNOKa3QGo67rFU2PhbFTp0uPJtilNji2Z0pMiSRQt3+OkIW0u3E1WMJqIycLqaTgb9JyFqH/S4mcTyyGtvyQ==} dependencies: cookie-es: 0.5.0 destr: 1.2.0 @@ -6513,7 +6513,7 @@ packages: fs-extra: 10.1.0 globby: 13.1.2 gzip-size: 7.0.0 - h3: 0.8.5 + h3: 0.8.6 hookable: 5.4.1 http-proxy: 1.18.1 is-primitive: 3.0.1 @@ -8485,7 +8485,7 @@ packages: anymatch: 3.1.2 chokidar: 3.5.3 destr: 1.2.0 - h3: 0.8.5 + h3: 0.8.6 ioredis: 5.2.3 listhen: 0.3.4 mkdir: 0.0.2