Skip to content

Commit

Permalink
fix router prefetch cache key to work with route interception
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Dec 22, 2023
1 parent b3ad907 commit 8120f81
Show file tree
Hide file tree
Showing 20 changed files with 246 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { addPathPrefix } from '../../../../shared/lib/router/utils/add-path-prefix'
import { pathHasPrefix } from '../../../../shared/lib/router/utils/path-has-prefix'
import { createHrefFromUrl } from '../create-href-from-url'

/**
* Creates a cache key for the router prefetch cache
*
* @param url - The URL being navigated to
* @param nextUrl - an internal URL, primarily used for handling rewrites. Defaults to '/'.
* @return The generated prefetch cache key.
*/
export function createPrefetchCacheKey(url: URL, nextUrl: string | null) {
const pathnameFromUrl = createHrefFromUrl(
url,
// Ensures the hash is not part of the cache key as it does not impact the server fetch
false
)

const nextUrlPrefix = `${nextUrl}%`

// Route interception depends on `nextUrl` values which aren't a 1:1 mapping to a URL
// The cache key that we store needs to use `nextUrl` to properly distinguish cache entries
if (nextUrl && !pathHasPrefix(pathnameFromUrl, nextUrlPrefix)) {
return addPathPrefix(pathnameFromUrl, nextUrlPrefix)
}

return pathnameFromUrl
}
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/linking/about",
"prefetchCache": Map {
"/linking/about" => {
"/linking%/linking/about" => {
"data": Promise {},
"kind": "temporary",
"lastUsedTime": 1690329600000,
Expand Down Expand Up @@ -448,7 +448,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/linking/about",
"prefetchCache": Map {
"/linking/about" => {
"/linking%/linking/about" => {
"data": Promise {},
"kind": "temporary",
"lastUsedTime": 1690329600000,
Expand Down Expand Up @@ -885,7 +885,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/linking",
"prefetchCache": Map {
"/linking" => {
"/linking%/linking" => {
"data": Promise {},
"kind": "temporary",
"lastUsedTime": 1690329600000,
Expand Down Expand Up @@ -1111,7 +1111,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/linking/about",
"prefetchCache": Map {
"/linking/about" => {
"/linking%/linking/about" => {
"data": Promise {},
"kind": "auto",
"lastUsedTime": 1690329600000,
Expand Down Expand Up @@ -1365,7 +1365,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/parallel-tab-bar/demographics",
"prefetchCache": Map {
"/parallel-tab-bar/demographics" => {
"/parallel-tab-bar%/parallel-tab-bar/demographics" => {
"data": Promise {},
"kind": "temporary",
"lastUsedTime": 1690329600000,
Expand Down Expand Up @@ -1708,7 +1708,7 @@ describe('navigateReducer', () => {
},
"nextUrl": "/linking/about",
"prefetchCache": Map {
"/linking/about" => {
"/linking%/linking/about" => {
"data": Promise {},
"kind": "temporary",
"lastUsedTime": 1690329600000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
listenForDynamicRequest,
updateCacheNodeOnNavigation,
} from '../ppr-navigations'
import { createPrefetchCacheKey } from './create-prefetch-cache-key'

export function handleExternalUrl(
state: ReadonlyReducerState,
Expand Down Expand Up @@ -126,7 +127,8 @@ function navigateReducer_noPPR(
return handleExternalUrl(state, mutable, url.toString(), pendingPush)
}

let prefetchValues = state.prefetchCache.get(createHrefFromUrl(url, false))
const prefetchCacheKey = createPrefetchCacheKey(url, state.nextUrl)
let prefetchValues = state.prefetchCache.get(prefetchCacheKey)

// If we don't have a prefetch value, we need to create one
if (!prefetchValues) {
Expand All @@ -152,7 +154,7 @@ function navigateReducer_noPPR(
lastUsedTime: null,
}

state.prefetchCache.set(createHrefFromUrl(url, false), newPrefetchValue)
state.prefetchCache.set(prefetchCacheKey, newPrefetchValue)
prefetchValues = newPrefetchValue
}

Expand Down Expand Up @@ -320,7 +322,8 @@ function navigateReducer_PPR(
return handleExternalUrl(state, mutable, url.toString(), pendingPush)
}

let prefetchValues = state.prefetchCache.get(createHrefFromUrl(url, false))
const prefetchCacheKey = createPrefetchCacheKey(url, state.nextUrl)
let prefetchValues = state.prefetchCache.get(prefetchCacheKey)

// If we don't have a prefetch value, we need to create one
if (!prefetchValues) {
Expand All @@ -346,7 +349,7 @@ function navigateReducer_PPR(
lastUsedTime: null,
}

state.prefetchCache.set(createHrefFromUrl(url, false), newPrefetchValue)
state.prefetchCache.set(prefetchCacheKey, newPrefetchValue)
prefetchValues = newPrefetchValue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe('prefetchReducer', () => {
buildId: 'development',
prefetchCache: new Map([
[
'/linking/about',
'/linking%/linking/about',
{
data: prom,
kind: PrefetchKind.AUTO,
Expand Down Expand Up @@ -302,7 +302,7 @@ describe('prefetchReducer', () => {
buildId: 'development',
prefetchCache: new Map([
[
'/linking/about',
'/linking%/linking/about',
{
data: prom,
prefetchTime: expect.any(Number),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { createHrefFromUrl } from '../create-href-from-url'
import { fetchServerResponse } from '../fetch-server-response'
import type {
PrefetchAction,
Expand All @@ -9,6 +8,7 @@ import { PrefetchKind } from '../router-reducer-types'
import { prunePrefetchCache } from './prune-prefetch-cache'
import { NEXT_RSC_UNION_QUERY } from '../../app-router-headers'
import { PromiseQueue } from '../../promise-queue'
import { createPrefetchCacheKey } from './create-prefetch-cache-key'

export const prefetchQueue = new PromiseQueue(5)

Expand All @@ -22,20 +22,16 @@ export function prefetchReducer(
const { url } = action
url.searchParams.delete(NEXT_RSC_UNION_QUERY)

const href = createHrefFromUrl(
url,
// Ensures the hash is not part of the cache key as it does not affect fetching the server
false
)
const prefetchCacheKey = createPrefetchCacheKey(url, state.nextUrl)
const cacheEntry = state.prefetchCache.get(prefetchCacheKey)

const cacheEntry = state.prefetchCache.get(href)
if (cacheEntry) {
/**
* If the cache entry present was marked as temporary, it means that we prefetched it from the navigate reducer,
* where we didn't have the prefetch intent. We want to update it to the new, more accurate, kind here.
*/
if (cacheEntry.kind === PrefetchKind.TEMPORARY) {
state.prefetchCache.set(href, {
state.prefetchCache.set(prefetchCacheKey, {
...cacheEntry,
kind: action.kind,
})
Expand Down Expand Up @@ -68,7 +64,7 @@ export function prefetchReducer(
)

// Create new tree based on the flightSegmentPath and router state patch
state.prefetchCache.set(href, {
state.prefetchCache.set(prefetchCacheKey, {
// Create new tree based on the flightSegmentPath and router state patch
treeAtTimeOfPrefetch: state.tree,
data: serverResponse,
Expand Down
27 changes: 27 additions & 0 deletions test/e2e/app-dir/interception-route-prefetch-cache/app/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client'

import { useRouter } from 'next/navigation'

interface ModalProps {
title: string
context: string
}

export function Modal({ title, context }: ModalProps) {
const router = useRouter()

return (
<div>
<div className="modal">
<h1>{title}</h1>
<h2>{context}</h2>
</div>
<div
className="modal-overlay"
onClick={() => {
router.back()
}}
/>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Modal } from '../../../../Modal'

export default function BarPagePostInterceptSlot({
params: { id },
}: {
params: {
id: string
}
}) {
return <Modal title={`Post ${id}`} context="Intercepted on Bar Page" />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function BarLayout({
modal,
children,
}: {
modal: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<div id="slot">{modal}</div>
<div id="children">{children}</div>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Link from 'next/link'

export default function BarPage() {
return (
<div>
<h1>Bar Page</h1>
<Link href="/post/1">Post 1</Link>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Modal } from '../../../../Modal'

export default function FooPagePostInterceptSlot({
params: { id },
}: {
params: {
id: string
}
}) {
return <Modal title={`Post ${id}`} context="Intercepted on Foo Page" />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function FooLayout({
modal,
children,
}: {
modal: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<div id="slot">{modal}</div>
<div id="children">{children}</div>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Link from 'next/link'

export default function FooPage() {
return (
<div>
<h1>Foo Page</h1>
<Link href="/post/1">Post 1</Link>
</div>
)
}
13 changes: 13 additions & 0 deletions test/e2e/app-dir/interception-route-prefetch-cache/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Link from 'next/link'

export default function RootLayout({ children }) {
return (
<html>
<head />
<body>
<Link href="/">home</Link>
{children}
</body>
</html>
)
}
18 changes: 18 additions & 0 deletions test/e2e/app-dir/interception-route-prefetch-cache/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Link from 'next/link'

export default function Home() {
return (
<ul>
<li>
<Link href="/foo">foo</Link>
</li>
<li>
<Link href="/bar">bar</Link>
</li>
<br />
<li>
<Link href="/post/1">post 1</Link>
</li>
</ul>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default function PostPage({
params: { id },
}: {
params: {
id: string
}
}) {
return (
<div>
<h1>Post {id}</h1>
</div>
)
}
Loading

0 comments on commit 8120f81

Please sign in to comment.