From 7a985070d80d59f71cedcd482a9ac3e943a03010 Mon Sep 17 00:00:00 2001 From: Zack Tanner <1939140+ztanner@users.noreply.github.com> Date: Wed, 18 Sep 2024 09:20:33 -0700 Subject: [PATCH] modify prefetch cache entry type based on redirect response --- .../reducers/server-action-reducer.ts | 9 ++++++++- test/e2e/app-dir/actions/app-action.test.ts | 20 +++++++++++++------ test/e2e/app-dir/app-basepath/index.test.ts | 12 +++++++---- .../parallel-routes-revalidation.test.ts | 6 ++++-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts index 257685e15d281e..803dc7f54175f9 100644 --- a/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts +++ b/packages/next/src/client/components/router-reducer/reducers/server-action-reducer.ts @@ -5,6 +5,7 @@ import type { import { callServer } from '../../../app-call-server' import { ACTION_HEADER, + NEXT_IS_PRERENDER_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_URL, RSC_CONTENT_TYPE_HEADER, @@ -53,6 +54,7 @@ type FetchServerActionResult = { redirectLocation: URL | undefined actionResult?: ActionResult actionFlightData?: NormalizedFlightData[] | string + isPrerender: boolean revalidatedParts: { tag: boolean cookie: boolean @@ -90,6 +92,7 @@ async function fetchServerAction( }) const location = res.headers.get('x-action-redirect') + const isPrerender = !!res.headers.get(NEXT_IS_PRERENDER_HEADER) let revalidatedParts: FetchServerActionResult['revalidatedParts'] try { const revalidatedHeader = JSON.parse( @@ -132,6 +135,7 @@ async function fetchServerAction( actionFlightData: normalizeFlightData(response.f), redirectLocation, revalidatedParts, + isPrerender, } } @@ -140,6 +144,7 @@ async function fetchServerAction( actionFlightData: normalizeFlightData(response.f), redirectLocation, revalidatedParts, + isPrerender, } } @@ -158,6 +163,7 @@ async function fetchServerAction( return { redirectLocation, revalidatedParts, + isPrerender, } } @@ -191,6 +197,7 @@ export function serverActionReducer( actionResult, actionFlightData: flightData, redirectLocation, + isPrerender, }) => { // Make sure the redirection is a push instead of a replace. // Issue: https://github.com/vercel/next.js/issues/53911 @@ -242,7 +249,7 @@ export function serverActionReducer( tree: state.tree, prefetchCache: state.prefetchCache, nextUrl: state.nextUrl, - kind: PrefetchKind.FULL, + kind: isPrerender ? PrefetchKind.FULL : PrefetchKind.AUTO, }) mutable.prefetchCache = state.prefetchCache diff --git a/test/e2e/app-dir/actions/app-action.test.ts b/test/e2e/app-dir/actions/app-action.test.ts index 4fa01ffbaa10c8..0f7a795c5679a9 100644 --- a/test/e2e/app-dir/actions/app-action.test.ts +++ b/test/e2e/app-dir/actions/app-action.test.ts @@ -957,9 +957,13 @@ describe('app-dir action handling', () => { 'redirected' ) - // no other requests should be made - expect(requests).toHaveLength(1) - expect(responses).toHaveLength(1) + // This verifies the redirect & server response happens in a single roundtrip, + // if the redirect resource was static. In development, these responses are always + // dynamically generated, so we only expect a single request for build/deploy. + if (!isNextDev) { + expect(requests).toHaveLength(1) + expect(responses).toHaveLength(1) + } const request = requests[0] const response = responses[0] @@ -1056,9 +1060,13 @@ describe('app-dir action handling', () => { await browser.elementById(`redirect-${redirectType}`).click() await check(() => browser.url(), `${next.url}${destinationPagePath}`) - // no other requests should be made - expect(requests).toHaveLength(1) - expect(responses).toHaveLength(1) + // This verifies the redirect & server response happens in a single roundtrip, + // if the redirect resource was static. In development, these responses are always + // dynamically generated, so we only expect a single request for build/deploy. + if (!isNextDev) { + expect(requests).toHaveLength(1) + expect(responses).toHaveLength(1) + } const request = requests[0] const response = responses[0] diff --git a/test/e2e/app-dir/app-basepath/index.test.ts b/test/e2e/app-dir/app-basepath/index.test.ts index c66eaf4e5282bc..f7161723f8254b 100644 --- a/test/e2e/app-dir/app-basepath/index.test.ts +++ b/test/e2e/app-dir/app-basepath/index.test.ts @@ -3,7 +3,7 @@ import { check, retry } from 'next-test-utils' import type { Request, Response } from 'playwright' describe('app dir - basepath', () => { - const { next } = nextTestSetup({ + const { next, isNextDev } = nextTestSetup({ files: __dirname, dependencies: { sass: 'latest', @@ -132,9 +132,13 @@ describe('app dir - basepath', () => { expect(await browser.waitForElementByCss('#page-2').text()).toBe(`Page 2`) - // verify that the POST request was only made to the action handler - expect(requests).toHaveLength(1) - expect(responses).toHaveLength(1) + // This verifies the redirect & server response happens in a single roundtrip, + // if the redirect resource was static. In development, these responses are always + // dynamically generated, so we only expect a single request for build/deploy. + if (!isNextDev) { + expect(requests).toHaveLength(1) + expect(responses).toHaveLength(1) + } const request = requests[0] const response = responses[0] diff --git a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts index 257a02b897805f..cdbda05745b218 100644 --- a/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts +++ b/test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts @@ -2,7 +2,7 @@ import { nextTestSetup } from 'e2e-utils' import { check, retry } from 'next-test-utils' describe('parallel-routes-revalidation', () => { - const { next, isNextStart, isNextDeploy } = nextTestSetup({ + const { next, isNextDev, isNextStart, isNextDeploy } = nextTestSetup({ files: __dirname, }) @@ -447,7 +447,9 @@ describe('parallel-routes-revalidation', () => { await browser.waitForIdleNetwork() await retry(async () => { - expect(rscRequests.length).toBe(0) + if (!isNextDev) { + expect(rscRequests.length).toBe(0) + } if (isNextStart) { expect(prefetchRequests.length).toBe(4)