@@ -173,6 +173,11 @@ import type { RouteModule } from './route-modules/route-module'
173
173
import { FallbackMode , parseFallbackField } from '../lib/fallback'
174
174
import { toResponseCacheEntry } from './response-cache/utils'
175
175
import { scheduleOnNextTick } from '../lib/scheduler'
176
+ import { PrefetchCacheScopes } from './lib/prefetch-cache-scopes'
177
+ import {
178
+ runWithCacheScope ,
179
+ type CacheScopeStore ,
180
+ } from './async-storage/cache-scope'
176
181
177
182
export type FindComponentsResult = {
178
183
components : LoadComponentsReturnType
@@ -454,6 +459,14 @@ export default abstract class Server<
454
459
455
460
private readonly isAppPPREnabled : boolean
456
461
462
+ private readonly prefetchCacheScopesDev = new PrefetchCacheScopes ( )
463
+
464
+ /**
465
+ * This is used to persist cache scopes across
466
+ * prefetch -> full route requests for dynamic IO
467
+ * it's only fully used in dev
468
+ */
469
+
457
470
public constructor ( options : ServerOptions ) {
458
471
const {
459
472
dir = '.' ,
@@ -2082,6 +2095,11 @@ export default abstract class Server<
2082
2095
typeof query . __nextppronly !== 'undefined' &&
2083
2096
couldSupportPPR
2084
2097
2098
+ // When enabled, this will allow the use of the `?__nextppronly` query
2099
+ // to enable debugging of the fallback shell.
2100
+ const hasDebugFallbackShellQuery =
2101
+ hasDebugStaticShellQuery && query . __nextppronly === 'fallback'
2102
+
2085
2103
// This page supports PPR if it is marked as being `PARTIALLY_STATIC` in the
2086
2104
// prerender manifest and this is an app page.
2087
2105
const isRoutePPREnabled : boolean =
@@ -2106,6 +2124,8 @@ export default abstract class Server<
2106
2124
const isDebugDynamicAccesses =
2107
2125
isDebugStaticShell && this . renderOpts . dev === true
2108
2126
2127
+ const isDebugFallbackShell = hasDebugFallbackShellQuery && isRoutePPREnabled
2128
+
2109
2129
// If we're in minimal mode, then try to get the postponed information from
2110
2130
// the request metadata. If available, use it for resuming the postponed
2111
2131
// render.
@@ -2741,7 +2761,7 @@ export default abstract class Server<
2741
2761
}
2742
2762
}
2743
2763
2744
- const responseGenerator : ResponseGenerator = async ( {
2764
+ let responseGenerator : ResponseGenerator = async ( {
2745
2765
hasResolved,
2746
2766
previousCacheEntry,
2747
2767
isRevalidating,
@@ -2974,7 +2994,8 @@ export default abstract class Server<
2974
2994
const fallbackRouteParams =
2975
2995
isDynamic &&
2976
2996
isRoutePPREnabled &&
2977
- getRequestMeta ( req , 'didSetDefaultRouteMatches' )
2997
+ ( getRequestMeta ( req , 'didSetDefaultRouteMatches' ) ||
2998
+ isDebugFallbackShell )
2978
2999
? getFallbackRouteParams ( pathname )
2979
3000
: null
2980
3001
@@ -2991,6 +3012,54 @@ export default abstract class Server<
2991
3012
}
2992
3013
}
2993
3014
3015
+ if ( this . nextConfig . experimental . dynamicIO ) {
3016
+ const originalResponseGenerator = responseGenerator
3017
+
3018
+ responseGenerator = async (
3019
+ ...args : Parameters < typeof responseGenerator >
3020
+ ) : ReturnType < typeof responseGenerator > => {
3021
+ let cache : CacheScopeStore [ 'cache' ] | undefined
3022
+
3023
+ if ( this . renderOpts . dev ) {
3024
+ cache = this . prefetchCacheScopesDev . get ( urlPathname )
3025
+
3026
+ // we need to seed the prefetch cache scope in dev
3027
+ // since we did not have a prefetch cache available
3028
+ // and this is not a prefetch request
3029
+ if (
3030
+ ! cache &&
3031
+ ! isPrefetchRSCRequest &&
3032
+ routeModule ?. definition . kind === RouteKind . APP_PAGE
3033
+ ) {
3034
+ req . headers [ RSC_HEADER ] = '1'
3035
+ req . headers [ NEXT_ROUTER_PREFETCH_HEADER ] = '1'
3036
+
3037
+ cache = new Map ( )
3038
+
3039
+ await runWithCacheScope ( { cache } , ( ) =>
3040
+ originalResponseGenerator ( ...args )
3041
+ )
3042
+ this . prefetchCacheScopesDev . set ( urlPathname , cache )
3043
+
3044
+ delete req . headers [ RSC_HEADER ]
3045
+ delete req . headers [ NEXT_ROUTER_PREFETCH_HEADER ]
3046
+ }
3047
+ }
3048
+
3049
+ return runWithCacheScope ( { cache } , ( ) =>
3050
+ originalResponseGenerator ( ...args )
3051
+ ) . finally ( ( ) => {
3052
+ if ( this . renderOpts . dev ) {
3053
+ if ( isPrefetchRSCRequest ) {
3054
+ this . prefetchCacheScopesDev . set ( urlPathname , cache )
3055
+ } else {
3056
+ this . prefetchCacheScopesDev . del ( urlPathname )
3057
+ }
3058
+ }
3059
+ } )
3060
+ }
3061
+ }
3062
+
2994
3063
const cacheEntry = await this . responseCache . get (
2995
3064
ssgCacheKey ,
2996
3065
responseGenerator ,
0 commit comments