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.