From 4846dbf6f8ad79db0bc031695cf529363082a5e8 Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Tue, 16 Jul 2024 23:40:51 +0200 Subject: [PATCH 01/38] fix(nuxt): allow `getCachedData` to return undefined (#28187) --- packages/nuxt/src/app/composables/asyncData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 66c6d10a81..778dd48daf 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -60,10 +60,10 @@ export interface AsyncDataOptions< default?: () => DefaultT | Ref /** * Provide a function which returns cached data. - * A `null` or `undefined` return value will trigger a fetch. + * An `undefined` return value will trigger a fetch. * Default is `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]` which only caches data when payloadExtraction is enabled. */ - getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer + getCachedData?: (key: string, nuxtApp: NuxtApp) => NoInfer | undefined /** * A function that can be used to alter handler function result after resolving. * Do not use it along with the `pick` option. From 42ef331816aabf3ebea23fc9e6bd1ca46f0cec23 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Wed, 17 Jul 2024 12:13:56 +0100 Subject: [PATCH 02/38] fix(nuxt): hide unhandled error messages in prod (#28156) --- packages/nuxt/src/core/runtime/nitro/error.ts | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/packages/nuxt/src/core/runtime/nitro/error.ts b/packages/nuxt/src/core/runtime/nitro/error.ts index 89f7ef65c6..7620f96981 100644 --- a/packages/nuxt/src/core/runtime/nitro/error.ts +++ b/packages/nuxt/src/core/runtime/nitro/error.ts @@ -115,29 +115,34 @@ function normalizeError (error: any) { // temp fix for https://github.com/unjs/nitro/issues/759 // TODO: investigate vercel-edge not using unenv pollyfill const cwd = typeof process.cwd === 'function' ? process.cwd() : '/' - const stack = ((error.stack as string) || '') - .split('\n') - .splice(1) - .filter(line => line.includes('at ')) - .map((line) => { - const text = line - .replace(cwd + '/', './') - .replace('webpack:/', '') - .replace('file://', '') - .trim() - return { - text, - internal: - (line.includes('node_modules') && !line.includes('.cache')) || - line.includes('internal') || - line.includes('new Promise'), - } - }) + + // Hide details of unhandled/fatal errors in production + const hideDetails = !import.meta.dev && error.unhandled + + const stack = hideDetails + ? [] + : ((error.stack as string) || '') + .split('\n') + .splice(1) + .filter(line => line.includes('at ')) + .map((line) => { + const text = line + .replace(cwd + '/', './') + .replace('webpack:/', '') + .replace('file://', '') + .trim() + return { + text, + internal: + (line.includes('node_modules') && !line.includes('.cache')) || + line.includes('internal') || + line.includes('new Promise'), + } + }) const statusCode = error.statusCode || 500 - const statusMessage = - error.statusMessage ?? (statusCode === 404 ? 'Not Found' : '') - const message = error.message || error.toString() + const statusMessage = error.statusMessage ?? (statusCode === 404 ? 'Not Found' : '') + const message = hideDetails ? 'internal server error' : (error.message || error.toString()) return { stack, From 0c3cc4cf30b0049fd10a9610461eb5e5ab985498 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 18 Jul 2024 12:47:34 +0100 Subject: [PATCH 03/38] fix(nuxt): add `useScriptCrisp` scripts stub --- packages/nuxt/src/app/composables/script-stubs.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nuxt/src/app/composables/script-stubs.ts b/packages/nuxt/src/app/composables/script-stubs.ts index 469b1619ee..7fcf5df5e6 100644 --- a/packages/nuxt/src/app/composables/script-stubs.ts +++ b/packages/nuxt/src/app/composables/script-stubs.ts @@ -45,6 +45,10 @@ export function useScriptCloudflareWebAnalytics (...args: unknown[]) { renderStubMessage('useScriptCloudflareWebAnalytics') } // eslint-disable-next-line @typescript-eslint/no-unused-vars +export function useScriptCrisp (...args: unknown[]) { + renderStubMessage('useScriptCrisp') +} +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function useScriptFathomAnalytics (...args: unknown[]) { renderStubMessage('useScriptFathomAnalytics') } From eb31abe10e16cf5b4ad4bf246b676a87c246fd02 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Thu, 18 Jul 2024 15:52:40 +0100 Subject: [PATCH 04/38] fix(nuxt): use `addEventListener` to register cookie store listener (#28193) --- packages/nuxt/index.d.ts | 3 ++- packages/nuxt/src/app/composables/cookie.ts | 13 +++++++++--- test/basic.test.ts | 2 +- test/fixtures/basic/pages/cookies.vue | 23 ++++++++++++++------- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/nuxt/index.d.ts b/packages/nuxt/index.d.ts index db90cd08a0..5630f9aeb0 100644 --- a/packages/nuxt/index.d.ts +++ b/packages/nuxt/index.d.ts @@ -12,7 +12,8 @@ declare global { interface Window { cookieStore?: { - onchange: (event: any) => void + addEventListener: (type: 'change', listener: (event: any) => void) => void + removeEventListener: (type: 'change', listener: (event: any) => void) => void } } } diff --git a/packages/nuxt/src/app/composables/cookie.ts b/packages/nuxt/src/app/composables/cookie.ts index bdc9d93782..45fab10d47 100644 --- a/packages/nuxt/src/app/composables/cookie.ts +++ b/packages/nuxt/src/app/composables/cookie.ts @@ -83,13 +83,16 @@ export function useCookie (name: string, _opts?: const handleChange = (data: { value?: any, refresh?: boolean }) => { const value = data.refresh ? readRawCookies(opts)?.[name] : opts.decode(data.value) watchPaused = true - cookies[name] = cookie.value = value + cookie.value = value + cookies[name] = klona(value) nextTick(() => { watchPaused = false }) } let watchPaused = false - if (getCurrentScope()) { + const hasScope = !!getCurrentScope() + + if (hasScope) { onScopeDispose(() => { watchPaused = true callback() @@ -98,10 +101,14 @@ export function useCookie (name: string, _opts?: } if (store) { - store.onchange = (event) => { + const changeHandler = (event: any) => { const cookie = event.changed.find((c: any) => c.name === name) if (cookie) { handleChange({ value: cookie.value }) } } + store.addEventListener('change', changeHandler) + if (hasScope) { + onScopeDispose(() => store.removeEventListener('change', changeHandler)) + } } else if (channel) { channel.onmessage = ({ data }) => handleChange(data) } diff --git a/test/basic.test.ts b/test/basic.test.ts index c97245b8bb..108e836075 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -591,7 +591,7 @@ describe('nuxt composables', () => { }, }) const cookies = res.headers.get('set-cookie') - expect(cookies).toMatchInlineSnapshot('"set-in-plugin=true; Path=/, set=set; Path=/, browser-set=set; Path=/, browser-set-to-null=; Max-Age=0; Path=/, browser-set-to-null-with-default=; Max-Age=0; Path=/, browser-object-default=%7B%22foo%22%3A%22bar%22%7D; Path=/"') + expect(cookies).toMatchInlineSnapshot('"set-in-plugin=true; Path=/, accessed-with-default-value=default; Path=/, set=set; Path=/, browser-set=set; Path=/, browser-set-to-null=; Max-Age=0; Path=/, browser-set-to-null-with-default=; Max-Age=0; Path=/, browser-object-default=%7B%22foo%22%3A%22bar%22%7D; Path=/"') }) it('updates cookies when they are changed', async () => { const { page } = await renderPage('/cookies') diff --git a/test/fixtures/basic/pages/cookies.vue b/test/fixtures/basic/pages/cookies.vue index d646cba627..742b6824bc 100644 --- a/test/fixtures/basic/pages/cookies.vue +++ b/test/fixtures/basic/pages/cookies.vue @@ -1,28 +1,37 @@ -