diff --git a/packages/next/src/client/components/app-router.tsx b/packages/next/src/client/components/app-router.tsx
index 1c17ad40e9be0..c9cf9c077403e 100644
--- a/packages/next/src/client/components/app-router.tsx
+++ b/packages/next/src/client/components/app-router.tsx
@@ -147,11 +147,14 @@ function HistoryUpdater({
return null
}
-export const createEmptyCacheNode = () => ({
- lazyData: null,
- rsc: null,
- parallelRoutes: new Map(),
-})
+export function createEmptyCacheNode(): CacheNode {
+ return {
+ lazyData: null,
+ rsc: null,
+ prefetchRsc: null,
+ parallelRoutes: new Map(),
+ }
+}
function useServerActionDispatcher(dispatch: React.Dispatch) {
const serverActionDispatcher: ServerActionDispatcher = useCallback(
diff --git a/packages/next/src/client/components/layout-router.tsx b/packages/next/src/client/components/layout-router.tsx
index 109ffa68c8e6a..f30c349b7c393 100644
--- a/packages/next/src/client/components/layout-router.tsx
+++ b/packages/next/src/client/components/layout-router.tsx
@@ -353,6 +353,9 @@ function InnerLayoutRouter({
// TODO-APP: remove ''
const refetchTree = walkAddRefetch(['', ...segmentPath], fullTree)
+ // TODO: Since this case always suspends indefinitely, and the only thing
+ // we're doing here is setting `lazyData`, it would be fine to mutate the
+ // current cache node (if it exists) rather than cloning it.
childNode = {
lazyData: fetchServerResponse(
new URL(url, location.origin),
@@ -361,6 +364,7 @@ function InnerLayoutRouter({
buildId
),
rsc: null,
+ prefetchRsc: childNode ? childNode.prefetchRsc : null,
head: childNode ? childNode.head : undefined,
parallelRoutes: childNode ? childNode.parallelRoutes : new Map(),
}
diff --git a/packages/next/src/client/components/router-reducer/apply-flight-data.ts b/packages/next/src/client/components/router-reducer/apply-flight-data.ts
index fabf4132c40e5..5003f47cbf5cf 100644
--- a/packages/next/src/client/components/router-reducer/apply-flight-data.ts
+++ b/packages/next/src/client/components/router-reducer/apply-flight-data.ts
@@ -20,6 +20,12 @@ export function applyFlightData(
if (flightDataPath.length === 3) {
const rsc = cacheNodeSeedData[2]
cache.rsc = rsc
+ // This is a PPR-only field. When PPR is enabled, we shouldn't hit
+ // this path during a navigation, but until PPR is fully implemented
+ // yet it's possible the existing node does have a non-null
+ // `prefetchRsc`. As an incremental step, we'll just de-opt to the
+ // old behavior — no PPR value.
+ cache.prefetchRsc = null
fillLazyItemsTillLeafWithHead(
cache,
existingCache,
@@ -31,6 +37,10 @@ export function applyFlightData(
} else {
// Copy rsc for the root node of the cache.
cache.rsc = existingCache.rsc
+ // This is a PPR-only field. Unlike the previous branch, since we're
+ // just cloning the existing cache node, we might as well keep the
+ // PPR value, if it exists.
+ cache.prefetchRsc = existingCache.prefetchRsc
cache.parallelRoutes = new Map(existingCache.parallelRoutes)
// Create a copy of the existing cache with the rsc applied.
fillCacheWithNewSubTreeData(
diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx
index 63e404383a68b..48067db2c07a6 100644
--- a/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx
+++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.test.tsx
@@ -57,6 +57,7 @@ describe('createInitialRouterState', () => {
const expectedCache: CacheNode = {
lazyData: null,
rsc: children,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -73,6 +74,7 @@ describe('createInitialRouterState', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: Test,
},
@@ -82,6 +84,7 @@ describe('createInitialRouterState', () => {
]),
lazyData: null,
rsc: null,
+ prefetchRsc: null,
},
],
]),
diff --git a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts
index 66ac0f2f6f7a6..deaca80dc00ff 100644
--- a/packages/next/src/client/components/router-reducer/create-initial-router-state.ts
+++ b/packages/next/src/client/components/router-reducer/create-initial-router-state.ts
@@ -35,6 +35,7 @@ export function createInitialRouterState({
const cache: CacheNode = {
lazyData: null,
rsc: rsc,
+ prefetchRsc: null,
// The cache gets seeded during the first render. `initialParallelRoutes` ensures the cache from the first render is there during the second render.
parallelRoutes: isServer ? new Map() : initialParallelRoutes,
}
diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx
index 8efbba3505d5c..4e38fbe9235f2 100644
--- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx
+++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.test.tsx
@@ -24,11 +24,13 @@ describe('fillCacheWithDataProperty', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
const existingCache: CacheNode = {
lazyData: null,
rsc: <>Root layout>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -38,6 +40,7 @@ describe('fillCacheWithDataProperty', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -47,6 +50,7 @@ describe('fillCacheWithDataProperty', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -76,12 +80,14 @@ describe('fillCacheWithDataProperty', () => {
"" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Page
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking
,
@@ -89,10 +95,12 @@ describe('fillCacheWithDataProperty', () => {
"dashboard" => {
"lazyData": Promise {},
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc": null,
}
`)
diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts
index af39cbb533cd5..98c247b6d2053 100644
--- a/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts
+++ b/packages/next/src/client/components/router-reducer/fill-cache-with-data-property.ts
@@ -40,6 +40,7 @@ export function fillCacheWithDataProperty(
childSegmentMap.set(cacheKey, {
lazyData: fetchResponse(),
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
})
}
@@ -52,6 +53,7 @@ export function fillCacheWithDataProperty(
childSegmentMap.set(cacheKey, {
lazyData: fetchResponse(),
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
})
}
@@ -62,6 +64,7 @@ export function fillCacheWithDataProperty(
childCacheNode = {
lazyData: childCacheNode.lazyData,
rsc: childCacheNode.rsc,
+ prefetchRsc: childCacheNode.prefetchRsc,
parallelRoutes: new Map(childCacheNode.parallelRoutes),
} as CacheNode
childSegmentMap.set(cacheKey, childCacheNode)
diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
index a331760b92444..6cf7cf2bc608a 100644
--- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
+++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.test.tsx
@@ -29,11 +29,13 @@ describe('fillCacheWithNewSubtreeData', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
const existingCache: CacheNode = {
lazyData: null,
rsc: <>Root layout>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -43,6 +45,7 @@ describe('fillCacheWithNewSubtreeData', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -52,6 +55,7 @@ describe('fillCacheWithNewSubtreeData', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -79,6 +83,7 @@ describe('fillCacheWithNewSubtreeData', () => {
const expectedCache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -88,6 +93,7 @@ describe('fillCacheWithNewSubtreeData', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -98,6 +104,7 @@ describe('fillCacheWithNewSubtreeData', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -114,6 +121,7 @@ describe('fillCacheWithNewSubtreeData', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -126,6 +134,7 @@ describe('fillCacheWithNewSubtreeData', () => {
],
]),
rsc: SubTreeData Injected!
,
+ prefetchRsc: null,
},
],
]),
diff --git a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts
index 67bd53b1a9495..78768ad778d94 100644
--- a/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts
+++ b/packages/next/src/client/components/router-reducer/fill-cache-with-new-subtree-data.ts
@@ -50,6 +50,7 @@ export function fillCacheWithNewSubTreeData(
childCacheNode = {
lazyData: null,
rsc,
+ prefetchRsc: null,
// Ensure segments other than the one we got data for are preserved.
parallelRoutes: existingChildCacheNode
? new Map(existingChildCacheNode.parallelRoutes)
@@ -88,6 +89,7 @@ export function fillCacheWithNewSubTreeData(
childCacheNode = {
lazyData: childCacheNode.lazyData,
rsc: childCacheNode.rsc,
+ prefetchRsc: childCacheNode.prefetchRsc,
parallelRoutes: new Map(childCacheNode.parallelRoutes),
} as CacheNode
childSegmentMap.set(cacheKey, childCacheNode)
diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx
index 28b075a069d75..c8b22c5539ed8 100644
--- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx
+++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.test.tsx
@@ -38,11 +38,13 @@ describe('fillLazyItemsTillLeafWithHead', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
const existingCache: CacheNode = {
lazyData: null,
rsc: <>Root layout>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -52,6 +54,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -61,6 +64,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -94,6 +98,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
const expectedCache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -103,6 +108,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -120,6 +126,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -132,6 +139,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
],
]),
rsc: null,
+ prefetchRsc: null,
},
],
[
@@ -139,6 +147,7 @@ describe('fillLazyItemsTillLeafWithHead', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
diff --git a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts
index 0b67e49f2c1e7..691ec50fa2ecc 100644
--- a/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts
+++ b/packages/next/src/client/components/router-reducer/fill-lazy-items-till-leaf-with-head.ts
@@ -53,6 +53,12 @@ export function fillLazyItemsTillLeafWithHead(
newCacheNode = {
lazyData: null,
rsc: seedNode,
+ // This is a PPR-only field. When PPR is enabled, we shouldn't hit
+ // this path during a navigation, but until PPR is fully implemented
+ // yet it's possible the existing node does have a non-null
+ // `prefetchRsc`. As an incremental step, we'll just de-opt to the
+ // old behavior — no PPR value.
+ prefetchRsc: null,
parallelRoutes: new Map(existingCacheNode?.parallelRoutes),
}
} else if (wasPrefetched && existingCacheNode) {
@@ -61,6 +67,10 @@ export function fillLazyItemsTillLeafWithHead(
newCacheNode = {
lazyData: existingCacheNode.lazyData,
rsc: existingCacheNode.rsc,
+ // This is a PPR-only field. Unlike the previous branch, since we're
+ // just cloning the existing cache node, we might as well keep the
+ // PPR value, if it exists.
+ prefetchRsc: existingCacheNode.prefetchRsc,
parallelRoutes: new Map(existingCacheNode.parallelRoutes),
} as CacheNode
} else {
@@ -69,6 +79,7 @@ export function fillLazyItemsTillLeafWithHead(
newCacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(existingCacheNode?.parallelRoutes),
}
}
@@ -97,6 +108,7 @@ export function fillLazyItemsTillLeafWithHead(
newCacheNode = {
lazyData: null,
rsc: seedNode,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
} else {
@@ -105,6 +117,7 @@ export function fillLazyItemsTillLeafWithHead(
newCacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
}
diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
index 9589560548657..4b175010b7637 100644
--- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
+++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.test.tsx
@@ -30,11 +30,13 @@ describe('invalidateCacheBelowFlightSegmentPath', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
const existingCache: CacheNode = {
lazyData: null,
rsc: <>Root layout>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -44,6 +46,7 @@ describe('invalidateCacheBelowFlightSegmentPath', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -53,6 +56,7 @@ describe('invalidateCacheBelowFlightSegmentPath', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -78,6 +82,7 @@ describe('invalidateCacheBelowFlightSegmentPath', () => {
// Copy rsc for the root node of the cache.
cache.rsc = existingCache.rsc
+ cache.prefetchRsc = existingCache.prefetchRsc
// Create a copy of the existing cache with the rsc applied.
fillCacheWithNewSubTreeData(cache, existingCache, flightDataPath, false)
@@ -108,18 +113,21 @@ describe('invalidateCacheBelowFlightSegmentPath', () => {
lazyData: null,
parallelRoutes: new Map(),
rsc: Page,
+ prefetchRsc: null,
},
],
]),
],
]),
rsc: Linking,
+ prefetchRsc: null,
},
],
]),
],
]),
rsc: <>Root layout>,
+ prefetchRsc: null,
}
expect(cache).toMatchObject(expectedCache)
diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts
index 4463189588baa..1f530701a35f6 100644
--- a/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts
+++ b/packages/next/src/client/components/router-reducer/invalidate-cache-below-flight-segmentpath.ts
@@ -49,6 +49,7 @@ export function invalidateCacheBelowFlightSegmentPath(
childCacheNode = {
lazyData: childCacheNode.lazyData,
rsc: childCacheNode.rsc,
+ prefetchRsc: childCacheNode.prefetchRsc,
parallelRoutes: new Map(childCacheNode.parallelRoutes),
} as CacheNode
childSegmentMap.set(cacheKey, childCacheNode)
diff --git a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx
index d18c0c67185ae..06cdaff97aa65 100644
--- a/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx
+++ b/packages/next/src/client/components/router-reducer/invalidate-cache-by-router-state.test.tsx
@@ -8,11 +8,13 @@ describe('invalidateCacheByRouterState', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
}
const existingCache: CacheNode = {
lazyData: null,
rsc: <>Root layout>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -22,6 +24,7 @@ describe('invalidateCacheByRouterState', () => {
{
lazyData: null,
rsc: <>Linking>,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -31,6 +34,7 @@ describe('invalidateCacheByRouterState', () => {
{
lazyData: null,
rsc: <>Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -69,6 +73,7 @@ describe('invalidateCacheByRouterState', () => {
const expectedCache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([['children', new Map()]]),
}
diff --git a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx
index 2415c1a3a858a..89be65c4265e9 100644
--- a/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/find-head-in-cache.test.tsx
@@ -28,6 +28,7 @@ describe('findHeadInCache', () => {
const cache: CacheNode = {
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -37,6 +38,7 @@ describe('findHeadInCache', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -54,6 +56,7 @@ describe('findHeadInCache', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -66,6 +69,7 @@ describe('findHeadInCache', () => {
],
]),
rsc: null,
+ prefetchRsc: null,
},
],
// TODO-APP: this segment should be preserved when creating the new cache
@@ -74,6 +78,7 @@ describe('findHeadInCache', () => {
// {
// lazyData: null,
// rsc: <>Page>,
+ // prefetchRsc: null,
// parallelRoutes: new Map(),
// },
// ],
diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx
index 4cfaa97aae6d8..1c9f93d2d0050 100644
--- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.test.tsx
@@ -141,6 +141,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -149,6 +150,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -190,6 +192,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -206,22 +209,26 @@ describe('navigateReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
About Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -327,6 +334,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -335,6 +343,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -377,6 +386,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -393,22 +403,26 @@ describe('navigateReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
About Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -514,6 +528,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -522,6 +537,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -567,18 +583,21 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -646,6 +665,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -654,6 +674,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -699,18 +720,21 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -778,6 +802,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -786,6 +811,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -828,18 +854,21 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -931,6 +960,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -939,6 +969,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -1018,6 +1049,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -1034,22 +1066,26 @@ describe('navigateReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
About Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -1171,6 +1207,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Audience Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -1184,6 +1221,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Views Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -1197,6 +1235,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Children Page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -1205,6 +1244,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -1247,6 +1287,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Audience Page
,
@@ -1263,10 +1304,12 @@ describe('navigateReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc": null,
},
},
@@ -1274,6 +1317,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Views Page
,
@@ -1283,16 +1327,19 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Children Page
,
},
},
},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -1414,6 +1461,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -1422,6 +1470,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -1464,18 +1513,21 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -1543,6 +1595,7 @@ describe('navigateReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -1551,6 +1604,7 @@ describe('navigateReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -1592,6 +1646,7 @@ describe('navigateReducer', () => {
"__PAGE__" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -1608,22 +1663,26 @@ describe('navigateReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
About Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
diff --git a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts
index 3ad4ace451ca8..e12e50c3a297a 100644
--- a/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts
+++ b/packages/next/src/client/components/router-reducer/reducers/navigate-reducer.ts
@@ -78,6 +78,7 @@ function addRefetchToLeafSegments(
let appliedPatch = false
newCache.rsc = currentCache.rsc
+ newCache.prefetchRsc = currentCache.prefetchRsc
newCache.parallelRoutes = new Map(currentCache.parallelRoutes)
const segmentPathsToFill = generateSegmentsFromPatch(treePatch).map(
@@ -239,6 +240,7 @@ export function navigateReducer(
if (hardNavigate) {
// Copy rsc for the root node of the cache.
cache.rsc = currentCache.rsc
+ cache.prefetchRsc = currentCache.prefetchRsc
invalidateCacheBelowFlightSegmentPath(
cache,
diff --git a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx
index 05f660c0d4897..e147c9e3346db 100644
--- a/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/prefetch-reducer.test.tsx
@@ -95,6 +95,7 @@ describe('prefetchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -103,6 +104,7 @@ describe('prefetchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -187,6 +189,7 @@ describe('prefetchReducer', () => {
Root layout
),
+ prefetchRsc: null,
parallelRoutes: initialParallelRoutes,
},
tree: [
@@ -234,6 +237,7 @@ describe('prefetchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -242,6 +246,7 @@ describe('prefetchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -340,6 +345,7 @@ describe('prefetchReducer', () => {
Root layout
),
+ prefetchRsc: null,
parallelRoutes: initialParallelRoutes,
},
tree: [
diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx
index 5e302a911b4bc..f7909760d7c59 100644
--- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.test.tsx
@@ -106,6 +106,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -114,6 +115,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -165,6 +167,7 @@ describe('refreshReducer', () => {
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -181,6 +184,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -194,6 +198,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: null,
+ prefetchRsc: null,
},
],
]),
@@ -244,6 +249,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -252,6 +258,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -317,6 +324,7 @@ describe('refreshReducer', () => {
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -333,6 +341,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -346,6 +355,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: null,
+ prefetchRsc: null,
},
],
]),
@@ -396,6 +406,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -404,6 +415,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
[
@@ -418,6 +430,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>About page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -426,6 +439,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>About layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -491,6 +505,7 @@ describe('refreshReducer', () => {
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -507,6 +522,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -520,6 +536,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: null,
+ prefetchRsc: null,
},
],
]),
@@ -570,6 +587,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -578,6 +596,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
[
@@ -592,6 +611,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: <>About page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -600,6 +620,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: <>About layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -714,6 +735,7 @@ describe('refreshReducer', () => {
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -730,6 +752,7 @@ describe('refreshReducer', () => {
{
lazyData: null,
rsc: null,
+ prefetchRsc: null,
parallelRoutes: new Map(),
head: (
<>
@@ -743,6 +766,7 @@ describe('refreshReducer', () => {
]),
lazyData: null,
rsc: null,
+ prefetchRsc: null,
},
],
]),
diff --git a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts
index 01a32295f6955..ed496f280b0b9 100644
--- a/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts
+++ b/packages/next/src/client/components/router-reducer/reducers/refresh-reducer.ts
@@ -96,6 +96,7 @@ export function refreshReducer(
if (cacheNodeSeedData !== null) {
const rsc = cacheNodeSeedData[2]
cache.rsc = rsc
+ cache.prefetchRsc = null
fillLazyItemsTillLeafWithHead(
cache,
// Existing cache is not passed in as `router.refresh()` has to invalidate the entire cache.
diff --git a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx
index da60183dcf379..b8c7f0dd78e4b 100644
--- a/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/restore-reducer.test.tsx
@@ -62,6 +62,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -70,6 +71,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -138,6 +140,7 @@ describe('serverPatchReducer', () => {
Root layout
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -154,6 +157,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -162,6 +166,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -212,6 +217,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -220,6 +226,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -301,6 +308,7 @@ describe('serverPatchReducer', () => {
Root layout
),
+ prefetchRsc: null,
parallelRoutes: new Map([
[
'children',
@@ -317,6 +325,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -325,6 +334,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
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 f58a3b5025222..ff9d618405710 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
@@ -244,6 +244,7 @@ export function serverActionReducer(
if (rsc !== null) {
const cache: CacheNode = createEmptyCacheNode()
cache.rsc = rsc
+ cache.prefetchRsc = null
fillLazyItemsTillLeafWithHead(
cache,
// Existing cache is not passed in as `router.refresh()` has to invalidate the entire cache.
diff --git a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx
index 68ed42456e975..5390076a8b006 100644
--- a/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx
+++ b/packages/next/src/client/components/router-reducer/reducers/server-patch-reducer.test.tsx
@@ -113,6 +113,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -121,6 +122,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -173,6 +175,7 @@ describe('serverPatchReducer', () => {
"" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -189,22 +192,26 @@ describe('serverPatchReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
Somewhere Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
@@ -277,6 +284,7 @@ describe('serverPatchReducer', () => {
{
lazyData: null,
rsc: <>Linking page>,
+ prefetchRsc: null,
parallelRoutes: new Map(),
},
],
@@ -285,6 +293,7 @@ describe('serverPatchReducer', () => {
]),
lazyData: null,
rsc: <>Linking layout level>,
+ prefetchRsc: null,
},
],
]),
@@ -349,6 +358,7 @@ describe('serverPatchReducer', () => {
"" => {
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc":
Linking page
,
@@ -365,10 +375,12 @@ describe('serverPatchReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
About Page!
,
@@ -385,22 +397,26 @@ describe('serverPatchReducer', () => {
,
"lazyData": null,
"parallelRoutes": Map {},
+ "prefetchRsc": null,
"rsc": null,
},
},
},
+ "prefetchRsc": null,
"rsc":
Somewhere Page!
,
},
},
},
+ "prefetchRsc": null,
"rsc":
Linking layout level
,
},
},
},
+ "prefetchRsc": null,
"rsc":
diff --git a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
index 43ea6c434e845..98a159e955973 100644
--- a/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
+++ b/packages/next/src/shared/lib/app-router-context.shared-runtime.ts
@@ -31,8 +31,16 @@ export type LazyCacheNode = {
*/
rsc: null
- // TODO: Add prefetchRsc field.
- // prefetchRsc: null
+ /**
+ * A prefetched version of the segment data. See explanation in corresponding
+ * field of ReadyCacheNode (below).
+ *
+ * Since LazyCacheNode mostly only exists in the non-PPR implementation, this
+ * will usually be null, but it could have been cloned from a previous
+ * CacheNode that was created by the PPR implementation. Eventually we want
+ * to migrate everything away from LazyCacheNode entirely.
+ */
+ prefetchRsc: React.ReactNode
/**
* A pending response for the lazy data fetch. If this is not present
@@ -61,8 +69,18 @@ export type ReadyCacheNode = {
*/
rsc: React.ReactNode
- // TODO: Add prefetchRsc field.
- // prefetchRsc: React.ReactNode
+ /**
+ * Represents a static version of the segment that can be showed immediately,
+ * and may or may not contain dynamic holes. It's prefetched before a
+ * navigation occurs.
+ *
+ * During rendering, we will choose whether to render `rsc` or `prefetchRsc`
+ * with `useDeferredValue`. As with the `rsc` field, a value of `null` means
+ * no value was provided. In this case, the LayoutRouter will go straight to
+ * rendering the `rsc` value; if that one is also missing, it will suspend and
+ * trigger a lazy fetch.
+ */
+ prefetchRsc: React.ReactNode
/**
* There should never be a lazy data request in this case.