Fix react fetch deduping without next cache (#50187)
This ensures we don't reference the wrong fetch global bypassing react's patched fetch so that it can properly fallback to react's fetch deduping when next cache is disabled for a specific fetch. x-ref: [slack thread](https://vercel.slack.com/archives/C042LHPJ1NX/p1684518670927189?thread_ts=1682974724.765039&cid=C042LHPJ1NX) fixes: https://github.com/vercel/next.js/issues/50031
This commit is contained in:
parent
f56722c971
commit
f4bb4aa73c
6 changed files with 98 additions and 6 deletions
|
@ -20,6 +20,7 @@ import type { StaticGenerationAsyncStorage } from '../client/components/static-g
|
|||
import '../server/require-hook'
|
||||
import '../server/node-polyfill-fetch'
|
||||
import '../server/node-polyfill-crypto'
|
||||
import '../server/node-environment'
|
||||
import chalk from 'next/dist/compiled/chalk'
|
||||
import getGzipSize from 'next/dist/compiled/gzip-size'
|
||||
import textTable from 'next/dist/compiled/text-table'
|
||||
|
@ -62,7 +63,6 @@ import { StaticGenerationAsyncStorageWrapper } from '../server/async-storage/sta
|
|||
import { IncrementalCache } from '../server/lib/incremental-cache'
|
||||
import { patchFetch } from '../server/lib/patch-fetch'
|
||||
import { nodeFs } from '../server/lib/node-fs-methods'
|
||||
import '../server/node-environment'
|
||||
import * as ciEnvironment from '../telemetry/ci-info'
|
||||
|
||||
export type ROUTER_TYPE = 'pages' | 'app'
|
||||
|
|
|
@ -73,10 +73,14 @@ export function patchFetch({
|
|||
serverHooks: typeof ServerHooks
|
||||
staticGenerationAsyncStorage: StaticGenerationAsyncStorage
|
||||
}) {
|
||||
if (!(globalThis as any)._nextOriginalFetch) {
|
||||
;(globalThis as any)._nextOriginalFetch = globalThis.fetch
|
||||
}
|
||||
|
||||
if ((globalThis.fetch as any).__nextPatched) return
|
||||
|
||||
const { DynamicServerError } = serverHooks
|
||||
const originFetch = globalThis.fetch
|
||||
const originFetch: typeof fetch = (globalThis as any)._nextOriginalFetch
|
||||
|
||||
globalThis.fetch = async (
|
||||
input: RequestInfo | URL,
|
||||
|
@ -527,8 +531,8 @@ export function patchFetch({
|
|||
}
|
||||
)
|
||||
}
|
||||
;(fetch as any).__nextGetStaticStore = () => {
|
||||
;(globalThis.fetch as any).__nextGetStaticStore = () => {
|
||||
return staticGenerationAsyncStorage
|
||||
}
|
||||
;(fetch as any).__nextPatched = true
|
||||
;(globalThis.fetch as any).__nextPatched = true
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// TODO: Remove use of `any` type.
|
||||
// Polyfill fetch() in the Node.js environment
|
||||
|
||||
if (!(global as any).fetch) {
|
||||
if (typeof fetch === 'undefined' && typeof globalThis.fetch === 'undefined') {
|
||||
function getFetchImpl() {
|
||||
return require('next/dist/compiled/undici')
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ if (!(global as any).fetch) {
|
|||
}
|
||||
|
||||
// Due to limitation of global configuration, we have to do this resolution at runtime
|
||||
;(global as any).fetch = (...args: any[]) => {
|
||||
globalThis.fetch = (...args: any[]) => {
|
||||
const fetchImpl = getFetchImpl()
|
||||
|
||||
// Undici does not support the `keepAlive` option,
|
||||
|
|
|
@ -35,6 +35,36 @@ createNextDescribe(
|
|||
}
|
||||
})
|
||||
|
||||
it.each([
|
||||
{
|
||||
path: '/react-fetch-deduping-node',
|
||||
},
|
||||
{
|
||||
path: '/react-fetch-deduping-edge',
|
||||
},
|
||||
])(
|
||||
'should correctly de-dupe fetch without next cache $path',
|
||||
async ({ path }) => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const res = await next.fetch(path, {
|
||||
redirect: 'manual',
|
||||
})
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
const html = await res.text()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const data1 = $('#data-1').text()
|
||||
const data2 = $('#data-2').text()
|
||||
|
||||
expect(data1).toBeTruthy()
|
||||
expect(data1).toBe(data2)
|
||||
|
||||
await waitFor(250)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it.each([
|
||||
{ pathname: '/unstable-cache-node' },
|
||||
{ pathname: '/unstable-cache-edge' },
|
||||
|
@ -506,6 +536,8 @@ createNextDescribe(
|
|||
'partial-gen-params-no-additional-slug/fr/second.html',
|
||||
'partial-gen-params-no-additional-slug/fr/second.rsc',
|
||||
'partial-gen-params/[lang]/[slug]/page.js',
|
||||
'react-fetch-deduping-edge/page.js',
|
||||
'react-fetch-deduping-node/page.js',
|
||||
'route-handler-edge/revalidate-360/route.js',
|
||||
'route-handler/post/route.js',
|
||||
'route-handler/revalidate-360-isr/route.js',
|
||||
|
|
29
test/e2e/app-dir/app-static/app/react-fetch-deduping-edge/page.js
vendored
Normal file
29
test/e2e/app-dir/app-static/app/react-fetch-deduping-edge/page.js
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
export const runtime = 'edge'
|
||||
|
||||
export default async function Page() {
|
||||
const data1 = await fetch(
|
||||
'https://next-data-api-endpoint.vercel.app/api/random?1',
|
||||
{
|
||||
next: {
|
||||
revalidate: 0,
|
||||
},
|
||||
}
|
||||
).then((res) => res.text())
|
||||
|
||||
const data2 = await fetch(
|
||||
'https://next-data-api-endpoint.vercel.app/api/random?1',
|
||||
{
|
||||
next: {
|
||||
revalidate: 0,
|
||||
},
|
||||
}
|
||||
).then((res) => res.text())
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>/react-fetch-deduping</p>
|
||||
<p id="data-1">{data1}</p>
|
||||
<p id="data-2">{data2}</p>
|
||||
</>
|
||||
)
|
||||
}
|
27
test/e2e/app-dir/app-static/app/react-fetch-deduping-node/page.js
vendored
Normal file
27
test/e2e/app-dir/app-static/app/react-fetch-deduping-node/page.js
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
export default async function Page() {
|
||||
const data1 = await fetch(
|
||||
'https://next-data-api-endpoint.vercel.app/api/random?1',
|
||||
{
|
||||
next: {
|
||||
revalidate: 0,
|
||||
},
|
||||
}
|
||||
).then((res) => res.text())
|
||||
|
||||
const data2 = await fetch(
|
||||
'https://next-data-api-endpoint.vercel.app/api/random?1',
|
||||
{
|
||||
next: {
|
||||
revalidate: 0,
|
||||
},
|
||||
}
|
||||
).then((res) => res.text())
|
||||
|
||||
return (
|
||||
<>
|
||||
<p>/react-fetch-deduping</p>
|
||||
<p id="data-1">{data1}</p>
|
||||
<p id="data-2">{data2}</p>
|
||||
</>
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue