diff --git a/packages/next/src/server/web/spec-extension/revalidate-tag.ts b/packages/next/src/server/web/spec-extension/revalidate-tag.ts index 620545987eae6..fcd3be4fb80c3 100644 --- a/packages/next/src/server/web/spec-extension/revalidate-tag.ts +++ b/packages/next/src/server/web/spec-extension/revalidate-tag.ts @@ -2,6 +2,7 @@ import type { StaticGenerationAsyncStorage, StaticGenerationStore, } from '../../../client/components/static-generation-async-storage.external' +import { staticGenerationBailout } from '../../../client/components/static-generation-bailout' export function revalidateTag(tag: string) { const staticGenerationAsyncStorage = ( @@ -16,6 +17,11 @@ export function revalidateTag(tag: string) { `Invariant: static generation store missing in revalidateTag ${tag}` ) } + + // a route that makes use of revalidation APIs should be considered dynamic + // as otherwise it would be impossible to revalidate + staticGenerationBailout(`revalidateTag ${tag}`) + if (!store.revalidatedTags) { store.revalidatedTags = [] } diff --git a/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-path/route.js b/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-path/route.js new file mode 100644 index 0000000000000..602822742a442 --- /dev/null +++ b/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-path/route.js @@ -0,0 +1,7 @@ +import { NextResponse } from 'next/server' +import { revalidatePath } from 'next/cache' + +export async function GET(req) { + revalidatePath('/') + return NextResponse.json({ revalidated: true, now: Date.now() }) +} diff --git a/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-tag/route.js b/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-tag/route.js new file mode 100644 index 0000000000000..5fc2a51a59a52 --- /dev/null +++ b/test/e2e/app-dir/revalidate-dynamic/app/api/revalidate-tag/route.js @@ -0,0 +1,7 @@ +import { NextResponse } from 'next/server' +import { revalidateTag } from 'next/cache' + +export async function GET(req) { + revalidateTag('thankyounext') + return NextResponse.json({ revalidated: true, now: Date.now() }) +} diff --git a/test/e2e/app-dir/revalidate-dynamic/app/layout.js b/test/e2e/app-dir/revalidate-dynamic/app/layout.js new file mode 100644 index 0000000000000..0ea378c6fcbbd --- /dev/null +++ b/test/e2e/app-dir/revalidate-dynamic/app/layout.js @@ -0,0 +1,7 @@ +export default function Layout({ children }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/revalidate-dynamic/app/page.js b/test/e2e/app-dir/revalidate-dynamic/app/page.js new file mode 100644 index 0000000000000..2d510fb21aba8 --- /dev/null +++ b/test/e2e/app-dir/revalidate-dynamic/app/page.js @@ -0,0 +1,17 @@ +export default async function Page() { + const data = await fetch( + 'https://next-data-api-endpoint.vercel.app/api/random', + { + next: { + tags: ['thankyounext'], + }, + } + ).then((res) => res.text()) + + return ( + <> + Data: +
{data}
+ + ) +} diff --git a/test/e2e/app-dir/revalidate-dynamic/revalidate-dynamic.test.ts b/test/e2e/app-dir/revalidate-dynamic/revalidate-dynamic.test.ts new file mode 100644 index 0000000000000..c084d67a216e9 --- /dev/null +++ b/test/e2e/app-dir/revalidate-dynamic/revalidate-dynamic.test.ts @@ -0,0 +1,36 @@ +import { createNextDescribe } from '../../../lib/e2e-utils' + +createNextDescribe( + 'app-dir revalidate-dynamic', + { + files: __dirname, + }, + ({ next, isNextStart }) => { + if (isNextStart) { + it('should correctly mark a route handler that uses revalidateTag as dynamic', async () => { + expect(next.cliOutput).toContain('λ /api/revalidate-path') + expect(next.cliOutput).toContain('λ /api/revalidate-tag') + }) + } + + it.each(['/api/revalidate-path', '/api/revalidate-tag'])( + `should revalidate the data with %s`, + async (path) => { + const browser = await next.browser('/') + const randomNumber = await browser.elementById('data-value').text() + await browser.refresh() + const randomNumber2 = await browser.elementById('data-value').text() + + expect(randomNumber).toEqual(randomNumber2) + + const revalidateRes = await next.fetch(path) + expect((await revalidateRes.json()).revalidated).toBe(true) + + await browser.refresh() + + const randomNumber3 = await browser.elementById('data-value').text() + expect(randomNumber).not.toEqual(randomNumber3) + } + ) + } +)