From 0fcb9e461ef05d4649c5f91d4639639048ad56bc Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sun, 14 Jan 2024 15:30:12 +0100 Subject: [PATCH 1/2] Add cache reason for using fetch with noStore --- ...tatic-generation-async-storage.external.ts | 1 + packages/next/src/server/lib/patch-fetch.ts | 3 +- .../web/spec-extension/unstable-no-store.ts | 4 + test/e2e/app-dir/logging/app/no-store/page.js | 21 +++++ .../e2e/app-dir/logging/fetch-logging.test.ts | 83 ++++++++----------- 5 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 test/e2e/app-dir/logging/app/no-store/page.js diff --git a/packages/next/src/client/components/static-generation-async-storage.external.ts b/packages/next/src/client/components/static-generation-async-storage.external.ts index 1ea0a0130a3f9..9ad563d3bbff2 100644 --- a/packages/next/src/client/components/static-generation-async-storage.external.ts +++ b/packages/next/src/client/components/static-generation-async-storage.external.ts @@ -51,6 +51,7 @@ export interface StaticGenerationStore { fetchMetrics?: FetchMetrics isDraftMode?: boolean + isUnstableNoStore?: boolean } export type StaticGenerationAsyncStorage = diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index 1cf98e9ba33af..29366766d8f66 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -287,6 +287,7 @@ export function patchFetch({ staticGenerationStore.fetchCache === 'only-no-store' const isForceNoStore = staticGenerationStore.fetchCache === 'force-no-store' + const isUsingNoStore = !!staticGenerationStore.isUnstableNoStore let _cache = getRequestMeta('cache') let cacheReason = '' @@ -387,7 +388,7 @@ export function patchFetch({ revalidate = 0 cacheReason = 'fetchCache = default-no-store' } else { - cacheReason = 'auto cache' + cacheReason = isUsingNoStore ? 'noStore call' : 'auto cache' revalidate = typeof staticGenerationStore.revalidate === 'boolean' || typeof staticGenerationStore.revalidate === 'undefined' diff --git a/packages/next/src/server/web/spec-extension/unstable-no-store.ts b/packages/next/src/server/web/spec-extension/unstable-no-store.ts index c7271aa9ed507..50484387d7a3a 100644 --- a/packages/next/src/server/web/spec-extension/unstable-no-store.ts +++ b/packages/next/src/server/web/spec-extension/unstable-no-store.ts @@ -10,6 +10,10 @@ export function unstable_noStore() { return } + // Mark the static generation context has unstable_noStore + if (staticGenerationStore) { + staticGenerationStore.isUnstableNoStore = true + } staticGenerationBailout('unstable_noStore', { link: 'https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering', }) diff --git a/test/e2e/app-dir/logging/app/no-store/page.js b/test/e2e/app-dir/logging/app/no-store/page.js new file mode 100644 index 0000000000000..7c7edbb662c63 --- /dev/null +++ b/test/e2e/app-dir/logging/app/no-store/page.js @@ -0,0 +1,21 @@ +import { unstable_noStore } from 'next/cache' + +async function getUncachedRandomData() { + unstable_noStore() + const res = await fetch( + 'https://next-data-api-endpoint.vercel.app/api/random?another-no-cache' + ) + const data = await res.json() + return data +} + +export default async function Page() { + const uncachedData = await getUncachedRandomData() + console.log('uncachedData', uncachedData) + return ( +
+

random: {Math.random()}

+

uncachedData: {uncachedData}

+
+ ) +} diff --git a/test/e2e/app-dir/logging/fetch-logging.test.ts b/test/e2e/app-dir/logging/fetch-logging.test.ts index 8402bc50868d8..d5c0ef341359d 100644 --- a/test/e2e/app-dir/logging/fetch-logging.test.ts +++ b/test/e2e/app-dir/logging/fetch-logging.test.ts @@ -1,7 +1,7 @@ import path from 'path' import fs from 'fs' import stripAnsi from 'strip-ansi' -import { check } from 'next-test-utils' +import { retry } from 'next-test-utils' import { createNextDescribe } from 'e2e-utils' function parseLogsFromCli(cliOutput: string) { @@ -56,18 +56,12 @@ createNextDescribe( const outputIndex = next.cliOutput.length await next.fetch('/default-cache') - await check(() => { + await retry(() => { const logs = stripAnsi(next.cliOutput.slice(outputIndex)) const hasLogs = logs.includes('GET /default-cache 200') - if (isNextDev && hasLogs) { - return 'success' - } - - if (!isNextDev && !hasLogs) { - return 'success' - } - }, 'success') + expect(isNextDev ? hasLogs : !hasLogs).toBe(true) + }) }) if (isNextDev) { @@ -75,7 +69,7 @@ createNextDescribe( const outputIndex = next.cliOutput.length await next.fetch('/default-cache') - await check(() => { + await retry(() => { const logs = parseLogsFromCli(next.cliOutput.slice(outputIndex)) const logEntry = logs.find((log) => @@ -86,26 +80,22 @@ createNextDescribe( !withFullUrlFetches ) - if (logEntry?.cache === 'cache: no-cache') { - return 'success' - } - }, 'success') + expect(logEntry?.cache).toBe('cache: no-cache') + }) }) it("should log 'skip' cache status with a reason when revalidate: 0 is used", async () => { const outputIndex = next.cliOutput.length await next.fetch('/default-cache') - await check(() => { + await retry(() => { const logs = parseLogsFromCli(next.cliOutput.slice(outputIndex)) const logEntry = logs.find((log) => log.url.includes('api/random?revalidate-0') ) - if (logEntry?.cache === 'revalidate: 0') { - return 'success' - } - }, 'success') + expect(logEntry?.cache).toBe('revalidate: 0') + }) }) it("should log 'skip' cache status with a reason when the browser indicates caching should be ignored", async () => { @@ -113,26 +103,24 @@ createNextDescribe( await next.fetch('/default-cache', { headers: { 'Cache-Control': 'no-cache' }, }) - await check(() => { + await retry(() => { const logs = parseLogsFromCli(next.cliOutput.slice(outputIndex)) const logEntry = logs.find((log) => log.url.includes('api/random?auto-cache') ) - if ( - logEntry?.cache === 'cache-control: no-cache (hard refresh)' - ) { - return 'success' - } - }, 'success') + expect(logEntry?.cache).toBe( + 'cache-control: no-cache (hard refresh)' + ) + }) }) it('should log requests with correct indentation', async () => { const outputIndex = next.cliOutput.length await next.fetch('/default-cache') - await check(() => { + await retry(() => { const logs = stripAnsi(next.cliOutput.slice(outputIndex)) const hasLogs = logs.includes(' GET /default-cache') && @@ -140,25 +128,30 @@ createNextDescribe( logs.includes(' │ │ GET ') && logs.includes(' │ │ Cache missed reason') - if (hasLogs) { - return 'success' - } - }, 'success') + expect(hasLogs).toBe(true) + }) + }) + + it('should show cache reason of noStore when use with fetch', async () => { + const logLength = next.cliOutput.length + await next.fetch('/no-store') + + await retry(() => { + const output = stripAnsi(next.cliOutput.slice(logLength)) + expect(output).toContain('Cache missed reason: (noStore call)') + }) }) } } else { + // No fetches logging enabled it('should not log fetch requests at all', async () => { const outputIndex = next.cliOutput.length await next.fetch('/default-cache') - await check(() => { + await retry(() => { const logs = stripAnsi(next.cliOutput.slice(outputIndex)) - if (logs.includes('GET /default-cache 200')) { - return 'fail' - } - - return 'success' - }, 'success') + expect(logs).not.toContain('GET /default-cache 200') + }) }) } @@ -167,28 +160,24 @@ createNextDescribe( const logLength = next.cliOutput.length await next.fetch('/') - await check(() => { + await retry(() => { const output = stripAnsi(next.cliOutput.slice(logLength)) expect(output).toContain('/') expect(output).not.toContain('/page') - - return 'success' - }, /success/) + }) }) it('should not contain metadata internal segments for dynamic metadata routes', async () => { const logLength = next.cliOutput.length await next.fetch('/dynamic/big/icon') - await check(() => { + await retry(() => { const output = stripAnsi(next.cliOutput.slice(logLength)) expect(output).toContain('/dynamic/[slug]/icon') expect(output).not.toContain('/(group)') expect(output).not.toContain('[[...__metadata_id__]]') expect(output).not.toContain('/route') - - return 'success' - }, /success/) + }) }) } } From 6ebfea5486a8c6bdd45037851c9dc19a78e249e6 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Sun, 14 Jan 2024 21:13:44 +0100 Subject: [PATCH 2/2] also set revalidate to 0 --- packages/next/src/server/lib/patch-fetch.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index 29366766d8f66..4b3d57fd5df25 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -387,8 +387,11 @@ export function patchFetch({ } else if (isDefaultNoStore) { revalidate = 0 cacheReason = 'fetchCache = default-no-store' + } else if (isUsingNoStore) { + revalidate = 0 + cacheReason = 'noStore call' } else { - cacheReason = isUsingNoStore ? 'noStore call' : 'auto cache' + cacheReason = 'auto cache' revalidate = typeof staticGenerationStore.revalidate === 'boolean' || typeof staticGenerationStore.revalidate === 'undefined'