@@ -635,8 +635,6 @@ function fetchRetry(
635635 } )
636636}
637637
638- const backgroundCache : Record < string , Promise < any > > = { }
639-
640638interface FetchDataOutput {
641639 dataHref : string
642640 json : Record < string , any > | null
@@ -687,7 +685,11 @@ function fetchNextData({
687685 const { href : cacheKey } = new URL ( dataHref , window . location . href )
688686 const getData = ( params ?: { method ?: 'HEAD' | 'GET' } ) =>
689687 fetchRetry ( dataHref , isServerRender ? 3 : 1 , {
690- headers : isPrefetch ? { purpose : 'prefetch' } : { } ,
688+ headers : Object . assign (
689+ { } as HeadersInit ,
690+ isPrefetch ? { purpose : 'prefetch' } : { } ,
691+ isPrefetch && hasMiddleware ? { 'x-middleware-prefetch' : '1' } : { }
692+ ) ,
691693 method : params ?. method ?? 'GET' ,
692694 } )
693695 . then ( ( response ) => {
@@ -756,7 +758,12 @@ function fetchNextData({
756758 return data
757759 } )
758760 . catch ( ( err ) => {
759- delete inflightCache [ cacheKey ]
761+ if ( ! unstable_skipClientCache ) {
762+ delete inflightCache [ cacheKey ]
763+ }
764+ if ( err . message === 'Failed to fetch' ) {
765+ markAssetError ( err )
766+ }
760767 throw err
761768 } )
762769
@@ -839,8 +846,10 @@ export default class Router implements BaseRouter {
839846 * Map of all components loaded in `Router`
840847 */
841848 components : { [ pathname : string ] : PrivateRouteInfo }
842- // Server Data Cache
849+ // Server Data Cache (full data requests)
843850 sdc : NextDataCache = { }
851+ // Server Background Cache (HEAD requests)
852+ sbc : NextDataCache = { }
844853
845854 sub : Subscription
846855 clc : ComponentLoadCancel
@@ -1966,6 +1975,7 @@ export default class Router implements BaseRouter {
19661975 ? existingInfo
19671976 : undefined
19681977
1978+ const isBackground = isQueryUpdating
19691979 const fetchNextDataParams : FetchNextDataParams = {
19701980 dataHref : this . pageLoader . getDataHref ( {
19711981 href : formatWithValidation ( { pathname, query } ) ,
@@ -1976,11 +1986,11 @@ export default class Router implements BaseRouter {
19761986 hasMiddleware : true ,
19771987 isServerRender : this . isSsr ,
19781988 parseJSON : true ,
1979- inflightCache : this . sdc ,
1989+ inflightCache : isBackground ? this . sbc : this . sdc ,
19801990 persistCache : ! isPreview ,
19811991 isPrefetch : false ,
19821992 unstable_skipClientCache,
1983- isBackground : isQueryUpdating ,
1993+ isBackground,
19841994 }
19851995
19861996 const data =
@@ -2071,26 +2081,36 @@ export default class Router implements BaseRouter {
20712081 )
20722082 }
20732083 }
2084+ const wasBailedPrefetch = data ?. response ?. headers . get ( 'x-middleware-skip' )
20742085
20752086 const shouldFetchData = routeInfo . __N_SSG || routeInfo . __N_SSP
20762087
2088+ // For non-SSG prefetches that bailed before sending data
2089+ // we clear the cache to fetch full response
2090+ if ( wasBailedPrefetch ) {
2091+ delete this . sdc [ data ?. dataHref ]
2092+ }
2093+
20772094 const { props, cacheKey } = await this . _getData ( async ( ) => {
20782095 if ( shouldFetchData ) {
2079- const { json, cacheKey : _cacheKey } = data ?. json
2080- ? data
2081- : await fetchNextData ( {
2082- dataHref : this . pageLoader . getDataHref ( {
2083- href : formatWithValidation ( { pathname, query } ) ,
2084- asPath : resolvedAs ,
2085- locale,
2086- } ) ,
2087- isServerRender : this . isSsr ,
2088- parseJSON : true ,
2089- inflightCache : this . sdc ,
2090- persistCache : ! isPreview ,
2091- isPrefetch : false ,
2092- unstable_skipClientCache,
2093- } )
2096+ const { json, cacheKey : _cacheKey } =
2097+ data ?. json && ! wasBailedPrefetch
2098+ ? data
2099+ : await fetchNextData ( {
2100+ dataHref :
2101+ data ?. dataHref ||
2102+ this . pageLoader . getDataHref ( {
2103+ href : formatWithValidation ( { pathname, query } ) ,
2104+ asPath : resolvedAs ,
2105+ locale,
2106+ } ) ,
2107+ isServerRender : this . isSsr ,
2108+ parseJSON : true ,
2109+ inflightCache : wasBailedPrefetch ? { } : this . sdc ,
2110+ persistCache : ! isPreview ,
2111+ isPrefetch : false ,
2112+ unstable_skipClientCache,
2113+ } )
20942114
20952115 return {
20962116 cacheKey : _cacheKey ,
@@ -2135,7 +2155,7 @@ export default class Router implements BaseRouter {
21352155 Object . assign ( { } , fetchNextDataParams , {
21362156 isBackground : true ,
21372157 persistCache : false ,
2138- inflightCache : backgroundCache ,
2158+ inflightCache : this . sbc ,
21392159 } )
21402160 ) . catch ( ( ) => { } )
21412161 }
@@ -2278,6 +2298,12 @@ export default class Router implements BaseRouter {
22782298 ? options . locale || undefined
22792299 : this . locale
22802300
2301+ const isMiddlewareMatch = await matchesMiddleware ( {
2302+ asPath : asPath ,
2303+ locale : locale ,
2304+ router : this ,
2305+ } )
2306+
22812307 if ( process . env . __NEXT_HAS_REWRITES && asPath . startsWith ( '/' ) ) {
22822308 let rewrites : any
22832309 ; ( { __rewrites : rewrites } = await getClientBuildManifest ( ) )
@@ -2305,7 +2331,9 @@ export default class Router implements BaseRouter {
23052331 pathname = rewritesResult . resolvedHref
23062332 parsed . pathname = pathname
23072333
2308- url = formatWithValidation ( parsed )
2334+ if ( ! isMiddlewareMatch ) {
2335+ url = formatWithValidation ( parsed )
2336+ }
23092337 }
23102338 }
23112339 parsed . pathname = resolveDynamicRoute ( parsed . pathname , pages )
@@ -2320,25 +2348,73 @@ export default class Router implements BaseRouter {
23202348 ) || { }
23212349 )
23222350
2323- url = formatWithValidation ( parsed )
2351+ if ( ! isMiddlewareMatch ) {
2352+ url = formatWithValidation ( parsed )
2353+ }
23242354 }
23252355
23262356 // Prefetch is not supported in development mode because it would trigger on-demand-entries
23272357 if ( process . env . NODE_ENV !== 'production' ) {
23282358 return
23292359 }
23302360
2361+ const data =
2362+ process . env . __NEXT_MIDDLEWARE_PREFETCH === 'strict'
2363+ ? ( { } as any )
2364+ : await withMiddlewareEffects ( {
2365+ fetchData : ( ) =>
2366+ fetchNextData ( {
2367+ dataHref : this . pageLoader . getDataHref ( {
2368+ href : formatWithValidation ( { pathname, query } ) ,
2369+ skipInterpolation : true ,
2370+ asPath : resolvedAs ,
2371+ locale,
2372+ } ) ,
2373+ hasMiddleware : true ,
2374+ isServerRender : this . isSsr ,
2375+ parseJSON : true ,
2376+ inflightCache : this . sdc ,
2377+ persistCache : ! this . isPreview ,
2378+ isPrefetch : true ,
2379+ } ) ,
2380+ asPath : asPath ,
2381+ locale : locale ,
2382+ router : this ,
2383+ } )
2384+
2385+ /**
2386+ * If there was a rewrite we apply the effects of the rewrite on the
2387+ * current parameters for the prefetch.
2388+ */
2389+ if ( data ?. effect . type === 'rewrite' ) {
2390+ parsed . pathname = data . effect . resolvedHref
2391+ pathname = data . effect . resolvedHref
2392+ query = { ...query , ...data . effect . parsedAs . query }
2393+ resolvedAs = data . effect . parsedAs . pathname
2394+ url = formatWithValidation ( parsed )
2395+ }
2396+
2397+ /**
2398+ * If there is a redirect to an external destination then we don't have
2399+ * to prefetch content as it will be unused.
2400+ */
2401+ if ( data ?. effect . type === 'redirect-external' ) {
2402+ return
2403+ }
2404+
23312405 const route = removeTrailingSlash ( pathname )
23322406
23332407 await Promise . all ( [
23342408 this . pageLoader . _isSsg ( route ) . then ( ( isSsg ) => {
23352409 return isSsg
23362410 ? fetchNextData ( {
2337- dataHref : this . pageLoader . getDataHref ( {
2338- href : url ,
2339- asPath : resolvedAs ,
2340- locale : locale ,
2341- } ) ,
2411+ dataHref :
2412+ data ?. dataHref ||
2413+ this . pageLoader . getDataHref ( {
2414+ href : url ,
2415+ asPath : resolvedAs ,
2416+ locale : locale ,
2417+ } ) ,
23422418 isServerRender : false ,
23432419 parseJSON : true ,
23442420 inflightCache : this . sdc ,
0 commit comments