Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ensure fetch is patched before preloading entries #72363

Merged
Merged
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
5 changes: 5 additions & 0 deletions packages/next/src/server/next-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ export default class NextNodeServer extends BaseServer<
for (const page of Object.keys(appPathsManifest || {})) {
await loadComponents({ distDir: this.distDir, page, isAppPath: true })
.then(async ({ ComponentMod }) => {
// we need to ensure fetch is patched before we require the page,
// otherwise if the fetch is patched by user code, we will be patching it
// too late and there won't be any caching behaviors
ComponentMod.patchFetch()

const webpackRequire = ComponentMod.__next_app__.require
if (webpackRequire?.m) {
for (const id of Object.keys(webpackRequire.m)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { waitFor, retry } from 'next-test-utils'
import { nextTestSetup } from 'e2e-utils'

describe('app-fetch-deduping', () => {
const { next } = nextTestSetup({ files: __dirname })

it('should still properly cache fetches when the user has a custom fetch implementation', async () => {
const browser = await next.browser('/')

let currentValue: string | undefined
await retry(async () => {
const initialRandom = await browser.elementById('random').text()
expect(initialRandom).toMatch(/^0\.\d+$/)

await browser.refresh()
currentValue = await browser.elementById('random').text()
expect(currentValue).toBe(initialRandom)
})

// wait for the revalidation period
await waitFor(3000)

await retry(async () => {
await browser.refresh()
const finalValue = await browser.elementById('random').text()
expect(finalValue).not.toBe(currentValue)
})
})
})
7 changes: 7 additions & 0 deletions test/production/app-dir/app-fetch-patching/app/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Layout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
36 changes: 36 additions & 0 deletions test/production/app-dir/app-fetch-patching/app/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { cookies } from 'next/headers'
import { Suspense } from 'react'

const patchFetch = (originalFetch) => async (url, options) => {
const res = await originalFetch(url, options)
return res
}

const customFetch = patchFetch(fetch)

export default async function Page() {
const data = await customFetch(
'https://next-data-api-endpoint.vercel.app/api/random?revalidate-3',
{
next: {
revalidate: 3,
},
}
).then((res) => res.text())

return (
<>
<div id="random">{data}</div>
<Suspense fallback={<div>Loading...</div>}>
<DynamicThing />
</Suspense>
</>
)
}

async function DynamicThing() {
const cookieStore = await cookies()
const cookie = cookieStore.get('name')

return <div>{cookie}</div>
}
Loading