Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { extractPathFromFlightRouterState } from './compute-changed-path'
import type { AppRouterState } from './router-reducer-types'
import { getFlightDataPartsFromPath } from '../../flight-data-helpers'
import { createInitialCacheNodeForHydration } from './ppr-navigations'
import { convertRootFlightRouterStateToRouteTree } from '../segment-cache/cache'
import type { NormalizedSearch } from '../segment-cache/cache-key'

export interface InitialRouterStateParameters {
navigatedAt: number
Expand Down Expand Up @@ -44,14 +46,41 @@ export function createInitialRouterState({
createHrefFromUrl(location)
: initialCanonicalUrl

// Conver the initial FlightRouterState into the RouteTree type.
// NOTE: The metadataVaryPath isn't used for anything currently because the
// head is embedded into the CacheNode tree, but eventually we'll lift it out
// and store it on the top-level state object.
const acc = { metadataVaryPath: null }
const initialRouteTree = convertRootFlightRouterStateToRouteTree(
initialTree,
initialRenderedSearch as NormalizedSearch,
acc
)
const initialTask = createInitialCacheNodeForHydration(
navigatedAt,
initialRouteTree,
initialSeedData,
initialHead
)

// NOTE: We intentionally don't check if any data needs to be fetched from the
// server. We assume the initial hydration payload is sufficient to render
// the page.
//
// The completeness of the initial data is an important property that we rely
// on as a last-ditch mechanism for recovering the app; we must always be able
// to reload a fresh HTML document to get to a consistent state.
//
// In the future, there may be cases where the server intentionally sends
// partial data and expects the client to fill in the rest, in which case this
// logic may change. (There already is a similar case where the server sends
// _no_ hydration data in the HTML document at all, and the client fetches it
// separately, but that's different because we still end up hydrating with a
// complete tree.)

const initialState = {
tree: initialTree,
cache: createInitialCacheNodeForHydration(
navigatedAt,
initialTree,
initialSeedData,
initialHead
),
tree: initialTask.route,
cache: initialTask.node,
pushRef: {
pendingPush: false,
mpaNavigation: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ import {
} from '../../flight-data-helpers'
import { getAppBuildId } from '../../app-build-id'
import { setCacheBustingSearchParam } from './set-cache-busting-search-param'
import {
getRenderedSearch,
urlToUrlWithoutFlightMarker,
} from '../../route-params'
import { urlToUrlWithoutFlightMarker } from '../../route-params'
import type { NormalizedSearch } from '../segment-cache/cache-key'
import { getDeploymentId } from '../../../shared/lib/deployment-id'

Expand Down Expand Up @@ -256,7 +253,14 @@ export async function fetchServerResponse(
return {
flightData: normalizedFlightData,
canonicalUrl: canonicalUrl,
renderedSearch: getRenderedSearch(res),
// TODO: We should be able to read this from the rewrite header, not the
// Flight response. Theoretically they should always agree, but there are
// currently some cases where it's incorrect for interception routes. We
// can always trust the value in the response body. However, per-segment
// prefetch responses don't embed the value in the body; they rely on the
// header alone. So we need to investigate why the header is sometimes
// wrong for interception routes.
renderedSearch: flightResponse.q as NormalizedSearch,
couldBeIntercepted: interception,
prerendered: flightResponse.S,
postponed,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { FlightRouterState } from '../../../shared/lib/app-router-types'
import type { RouteTree } from '../segment-cache/cache'

export function isNavigatingToNewRootLayout(
currentTree: FlightRouterState,
nextTree: FlightRouterState
nextTree: RouteTree
): boolean {
// Compare segments
const currentTreeSegment = currentTree[0]
const nextTreeSegment = nextTree[0]
const nextTreeSegment = nextTree.segment

// If any segment is different before we find the root layout, the root layout has changed.
// E.g. /same/(group1)/layout.js -> /same/(group2)/layout.js
Expand All @@ -27,17 +28,26 @@ export function isNavigatingToNewRootLayout(
// Current tree root layout found
if (currentTree[4]) {
// If the next tree doesn't have the root layout flag, it must have changed.
return !nextTree[4]
return !nextTree.isRootLayout
}
// Current tree didn't have its root layout here, must have changed.
if (nextTree[4]) {
if (nextTree.isRootLayout) {
return true
}
// We can't assume it's `parallelRoutes.children` here in case the root layout is `app/@something/layout.js`
// But it's not possible to be more than one parallelRoutes before the root layout is found
// TODO-APP: change to traverse all parallel routes
const currentTreeChild = Object.values(currentTree[1])[0]
const nextTreeChild = Object.values(nextTree[1])[0]
if (!currentTreeChild || !nextTreeChild) return true
return isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild)

const slots = nextTree.slots
const currentTreeChildren = currentTree[1]
if (slots !== null) {
for (const slot in slots) {
const nextTreeChild = slots[slot]
const currentTreeChild = currentTreeChildren[slot]
if (
currentTreeChild === undefined ||
isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild)
) {
return true
}
}
}
return true
}
Loading
Loading