Skip to content

Commit

Permalink
Fix fetch deduping in dev after reload (#66507)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Jun 4, 2024
1 parent 77e6b03 commit 52689b5
Show file tree
Hide file tree
Showing 4 changed files with 13 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as ReactJsxDevRuntime from 'react/jsx-dev-runtime'
import * as ReactJsxRuntime from 'react/jsx-runtime'
//@ts-expect-error TODO: current @types/react does not have exported types for this import
import * as ReactCompilerRuntime from 'react/compiler-runtime'
import '../../../../../next-fetch'

function getAltProxyForBindingsDEV(
type: 'Turbopack' | 'Webpack',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ function generateCacheKey(request: Request): string {
])
}

if (typeof fetch === 'function') {
const originalFetch = fetch
const cachedFetch = function fetch(
export function createDedupeFetch(originalFetch: typeof fetch) {
return function dedupeFetch(
resource: URL | RequestInfo,
options?: RequestInit
) {
Expand Down Expand Up @@ -99,23 +98,4 @@ if (typeof fetch === 'function') {
// of the body so that it can be read multiple times.
return match.then((response) => response.clone())
}
// We don't expect to see any extra properties on fetch but if there are any,
// copy them over. Useful for extended fetch environments or mocks.
Object.assign(cachedFetch, originalFetch)
try {
// @ts-ignore
// eslint-disable-next-line no-native-reassign
fetch = cachedFetch
} catch (error1) {
try {
// In case assigning it globally fails, try globalThis instead just in case it exists.
globalThis.fetch = cachedFetch
} catch (error2) {
// Log even in production just to make sure this is seen if only prod is frozen.
console.warn(
'Next.js was unable to patch the fetch() function in this environment. ' +
'Suspensey APIs might not work correctly as a result.'
)
}
}
}
3 changes: 2 additions & 1 deletion packages/next/src/server/lib/patch-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import * as Log from '../../build/output/log'
import { markCurrentScopeAsDynamic } from '../app-render/dynamic-rendering'
import type { FetchMetric } from '../base-http'
import { createDedupeFetch } from './dedupe-fetch'

const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'

Expand Down Expand Up @@ -788,7 +789,7 @@ export function patchFetch(options: PatchableModule) {

// Grab the original fetch function. We'll attach this so we can use it in
// the patched fetch function.
const original = globalThis.fetch
const original = createDedupeFetch(globalThis.fetch)

// Set the global fetch to the patched fetch.
globalThis.fetch = createPatchedFetcher(original, options)
Expand Down
13 changes: 9 additions & 4 deletions test/e2e/app-dir/app/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1482,14 +1482,19 @@ describe('app dir - basic', () => {

describe('should support React fetch instrumentation', () => {
it('server component', async () => {
// trigger compilation of 404 here first.
// Any other page being compiled between refresh of a page would get us fresh modules i.e. not catch previous regressions where we restored the wrong fetch.
await next.browser('/_not-found')
const browser = await next.browser('/react-fetch/server-component')
const val1 = await browser.elementByCss('#value-1').text()
const val2 = await browser.elementByCss('#value-2').text()
expect(val1).toBe(val2)

// TODO: enable when fetch cache is enabled in dev
if (!isDev) {
expect(val1).toBe(val2)
}
await browser.refresh()

const val1AfterRefresh = await browser.elementByCss('#value-1').text()
const val2AfterRefresh = await browser.elementByCss('#value-2').text()
expect(val1AfterRefresh).toBe(val2AfterRefresh)
})

it('server component client-navigation', async () => {
Expand Down

0 comments on commit 52689b5

Please sign in to comment.