Skip to content

Commit

Permalink
normalize flight data after receiving server response (#69460)
Browse files Browse the repository at this point in the history
This follows the work in #69241 by moving the spot where we normalize
the `FlightData` response with array slicing etc to a single spot after
we receive a response from the server.

This includes a refactor to `fillCacheWithNewSubTreeData` to not be
recursive and instead iterate over the segment path, copying unchanged
segments until finally performing the cache node insertion at the leaf
segment.
  • Loading branch information
ztanner committed Aug 29, 2024
1 parent b5408d1 commit 785997f
Show file tree
Hide file tree
Showing 18 changed files with 237 additions and 256 deletions.
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime'
import type { FlightDataPath } from '../../../server/app-render/types'
import { fillLazyItemsTillLeafWithHead } from './fill-lazy-items-till-leaf-with-head'
import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data'
import type { PrefetchCacheEntry } from './router-reducer-types'
import {
getFlightDataPartsFromPath,
isRootFlightDataPath,
} from '../../flight-data-helpers'
import type { NormalizedFlightData } from '../../flight-data-helpers'

export function applyFlightData(
existingCache: CacheNode,
cache: CacheNode,
flightDataPath: FlightDataPath,
flightData: NormalizedFlightData,
prefetchEntry?: PrefetchCacheEntry
): boolean {
// The one before last item is the router state tree patch
const {
tree: treePatch,
seedData,
head,
} = getFlightDataPartsFromPath(flightDataPath)
const { tree: treePatch, seedData, head, isRootRender } = flightData

// Handles case where prefetch only returns the router tree patch without rendered components.
if (seedData === null) {
return false
}

if (isRootFlightDataPath(flightDataPath)) {
if (isRootRender) {
const rsc = seedData[1]
const loading = seedData[3]
cache.loading = loading
Expand Down Expand Up @@ -55,12 +47,7 @@ export function applyFlightData(
cache.parallelRoutes = new Map(existingCache.parallelRoutes)
cache.loading = existingCache.loading
// Create a copy of the existing cache with the rsc applied.
fillCacheWithNewSubTreeData(
cache,
existingCache,
flightDataPath,
prefetchEntry
)
fillCacheWithNewSubTreeData(cache, existingCache, flightData, prefetchEntry)
}

return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ export function createInitialRouterState({
// This is to ensure that when the RSC payload streamed to the client, crawlers don't interpret it
// as a URL that should be crawled.
const initialCanonicalUrl = initialCanonicalUrlParts.join('/')
const normalizedFlightData = getFlightDataPartsFromPath(initialFlightData[0])
const {
tree: initialTree,
seedData: initialSeedData,
head: initialHead,
} = getFlightDataPartsFromPath(initialFlightData[0])
} = normalizedFlightData
const isServer = !location
// For the SSR render, seed data should always be available (we only send back a `null` response
// in the case of a `loading` segment, pre-PPR.)
Expand Down Expand Up @@ -114,7 +115,7 @@ export function createInitialRouterState({
createPrefetchCacheEntryForInitialLoad({
url,
data: {
flightData: initialFlightData,
flightData: [normalizedFlightData],
canonicalUrl: undefined,
couldBeIntercepted: !!couldBeIntercepted,
// TODO: the server should probably send a value for this. Default to false for now.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const { createFromFetch } = (
import type {
FlightRouterState,
NavigationFlightResponse,
FetchServerResponseResult,
} from '../../../server/app-render/types'
import {
NEXT_ROUTER_PREFETCH_HEADER,
Expand All @@ -30,6 +29,10 @@ import {
import { callServer } from '../../app-call-server'
import { PrefetchKind } from './router-reducer-types'
import { hexHash } from '../../../shared/lib/hash'
import {
normalizeFlightData,
type NormalizedFlightData,
} from '../../flight-data-helpers'

export interface FetchServerResponseOptions {
readonly flightRouterState: FlightRouterState
Expand All @@ -39,6 +42,14 @@ export interface FetchServerResponseOptions {
readonly isHmrRefresh?: boolean
}

export type FetchServerResponseResult = {
flightData: NormalizedFlightData[] | string
canonicalUrl: URL | undefined
couldBeIntercepted: boolean
isPrerender: boolean
postponed: boolean
}

function urlToUrlWithoutFlightMarker(url: string): URL {
const urlWithoutFlightParameters = new URL(url, location.origin)
urlWithoutFlightParameters.searchParams.delete(NEXT_RSC_UNION_QUERY)
Expand Down Expand Up @@ -209,7 +220,7 @@ export async function fetchServerResponse(
}

return {
flightData: response.f,
flightData: normalizeFlightData(response.f),
canonicalUrl: canonicalUrl,
couldBeIntercepted: interception,
isPrerender: isPrerender,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
import React from 'react'
import { fillCacheWithNewSubTreeData } from './fill-cache-with-new-subtree-data'
import type { CacheNode } from '../../../shared/lib/app-router-context.shared-runtime'
import type { FlightData } from '../../../server/app-render/types'
import type { NormalizedFlightData } from '../../flight-data-helpers'

const getFlightData = (): FlightData => {
const getFlightData = (): NormalizedFlightData[] => {
return [
[
'children',
'linking',
'children',
'about',
[
'about',
{
children: ['', {}],
},
],
['about', {}, <h1>SubTreeData Injected!</h1>],
<>
<title>Head Injected!</title>
</>,
],
{
pathToSegment: ['children', 'linking', 'children'],
segmentPath: ['children', 'linking', 'children', 'about'],
segment: 'about',
tree: ['about', { children: ['', {}] }],
seedData: ['about', <h1>SubTreeData Injected!</h1>, {}, null],
head: '<title>Head Injected!</title>',
isRootRender: false,
},
]
}

Expand Down Expand Up @@ -88,9 +81,9 @@ describe('fillCacheWithNewSubtreeData', () => {
}

// Mirrors the way router-reducer values are passed in.
const flightDataPath = flightData[0]
const normalizedFlightData = flightData[0]

fillCacheWithNewSubTreeData(cache, existingCache, flightDataPath)
fillCacheWithNewSubTreeData(cache, existingCache, normalizedFlightData)

const expectedCache: CacheNode = {
lazyData: null,
Expand Down
Loading

0 comments on commit 785997f

Please sign in to comment.