Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[Segment Cache] No data on tree prefetch if no PPR #73767

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
NEXT_ROUTER_STALE_TIME_HEADER,
NEXT_URL,
RSC_HEADER,
NEXT_ROUTER_SEGMENT_PREFETCH_HEADER,
} from '../../client/components/app-router-headers'
import {
createTrackedMetadataContext,
Expand Down Expand Up @@ -234,6 +235,7 @@ interface ParsedRequestHeaders {
*/
readonly flightRouterState: FlightRouterState | undefined
readonly isPrefetchRequest: boolean
readonly isRouteTreePrefetchRequest: boolean
readonly isDevWarmupRequest: boolean
readonly isHmrRefresh: boolean
readonly isRSCRequest: boolean
Expand Down Expand Up @@ -267,6 +269,10 @@ function parseRequestHeaders(
)
: undefined

// Checks if this is a prefetch of the Route Tree by the Segment Cache
const isRouteTreePrefetchRequest =
headers[NEXT_ROUTER_SEGMENT_PREFETCH_HEADER.toLowerCase()] === '/_tree'

const csp =
headers['content-security-policy'] ||
headers['content-security-policy-report-only']
Expand All @@ -277,6 +283,7 @@ function parseRequestHeaders(
return {
flightRouterState,
isPrefetchRequest,
isRouteTreePrefetchRequest,
isHmrRefresh,
isRSCRequest,
isDevWarmupRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export async function walkTreeWithFlightRouterState({
query,
isPrefetch,
getDynamicParamFromSegment,
parsedRequestHeaders,
} = ctx

const [segment, parallelRoutes, modules] = loaderTreeToFilter
Expand Down Expand Up @@ -115,9 +116,13 @@ export async function walkTreeWithFlightRouterState({
// somewhere in the tree, we'll recursively render the component tree up until we encounter that loading component, and then stop.
const shouldSkipComponentTree =
!experimental.isRoutePPREnabled &&
isPrefetch &&
!Boolean(modules.loading) &&
!hasLoadingComponentInTree(loaderTreeToFilter)
// If PPR is disabled, and this is a request for the route tree, then we
// never render any components. Only send the router state.
(parsedRequestHeaders.isRouteTreePrefetchRequest ||
// Otherwise, check for the presence of a `loading` component.
(isPrefetch &&
!Boolean(modules.loading) &&
!hasLoadingComponentInTree(loaderTreeToFilter)))

if (!parentRendered && renderComponentsOnThisLevel) {
const overriddenSegment =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Suspense } from 'react'
import { connection } from 'next/server'

async function Content() {
await connection()
return 'Dynamic Content'
}

export default function PPRDisabled() {
return (
<Suspense fallback="Loading...">
<Content />
</Suspense>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { connection } from 'next/server'

export default async function PPRDisabledWithLoadingBoundary() {
await connection()
return 'Dynamic Content'
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,28 @@ describe('segment cache (incremental opt in)', () => {
const result = extractPseudoJSONFromFlightResponse(flightText)
expect(typeof result.b === 'string').toBe(true)
})

// TODO: Replace with e2e test once the client part is implemented
it('route tree prefetch does not include any component data even if loading.tsx is defined', async () => {
await next.browser('/')
const response = await next.fetch('/ppr-disabled-with-loading-boundary', {
headers: {
RSC: '1',
'Next-Router-Prefetch': '1',
'Next-Router-Segment-Prefetch': '/_tree',
},
})
expect(response.status).toBe(200)
expect(response.headers.get('x-nextjs-postponed')).toBe(null)

// Usually when PPR is disabled, a prefetch to a route that has a
// loading.tsx boundary will include component data in the response, up to
// the first loading boundary. But since this is specifically a prefetch
// of the route tree, it should skip all the component data and only return
// the router state.
const flightText = await response.text()
// Confirm that the response does not include any component data by checking
// for the absence of the loading component.
expect(flightText).not.toContain('Loading...')
})
})
Loading