@@ -607,26 +607,6 @@ async function generateDynamicFlightRenderResult(
607607 onFlightDataRenderError
608608 )
609609
610- const RSCPayload : RSCPayload & {
611- /** Only available during cacheComponents development builds. Used for logging errors. */
612- _validation ?: Promise < ReactNode >
613- _bypassCachesInDev ?: React . ReactNode
614- } = await workUnitAsyncStorage . run (
615- requestStore ,
616- generateDynamicRSCPayload ,
617- ctx ,
618- options
619- )
620-
621- if (
622- process . env . NODE_ENV === 'development' &&
623- isBypassingCachesInDev ( renderOpts , requestStore )
624- ) {
625- RSCPayload . _bypassCachesInDev = createElement ( WarnForBypassCachesInDev , {
626- route : workStore . route ,
627- } )
628- }
629-
630610 const debugChannel = setReactDebugChannel && createDebugChannel ( )
631611
632612 if ( debugChannel ) {
@@ -635,63 +615,60 @@ async function generateDynamicFlightRenderResult(
635615
636616 let stream : ReadableStream < Uint8Array < ArrayBufferLike > >
637617 if ( process . env . NODE_ENV === 'development' && renderOpts . cacheComponents ) {
638- // If we got here in Cache Components dev, we're rendering while bypassing caches,
639- // so we have no hope of showing a useful runtime stage.
640- // But we still want things like `params` to show up in devtools correctly,
641- // which relies on mechanisms we've set up for staged rendering,
642- // so we do a 2-task version (Static -> Dynamic) instead.
643-
644- const stageController = new StagedRenderingController ( )
645-
646- const environmentName = ( ) => {
647- const currentStage = stageController . currentStage
648- switch ( currentStage ) {
649- case RenderStage . Static :
650- return 'Prerender'
651- case RenderStage . Runtime :
652- case RenderStage . Dynamic :
653- return 'Server'
654- default :
655- currentStage satisfies never
656- throw new InvariantError ( `Invalid render stage: ${ currentStage } ` )
618+ // If we got here, it means we didn't go into `generateDynamicFlightRenderResultWithCachesInDev`,
619+ // so caches must be disabled. Render dynamically, but include basic environment labels.
620+
621+ const getPayload = async (
622+ // eslint-disable-next-line @typescript-eslint/no-shadow
623+ requestStore : RequestStore
624+ ) : Promise < RSCPayload > => {
625+ const rscPayload : RSCPayload & {
626+ /** Only available during cacheComponents development builds. Used for logging errors. */
627+ _validation ?: Promise < ReactNode >
628+ _bypassCachesInDev ?: React . ReactNode
629+ } = await workUnitAsyncStorage . run (
630+ requestStore ,
631+ generateDynamicRSCPayload ,
632+ ctx ,
633+ options
634+ )
635+ if ( isBypassingCachesInDev ( renderOpts , requestStore ) ) {
636+ rscPayload . _bypassCachesInDev = createElement (
637+ WarnForBypassCachesInDev ,
638+ {
639+ route : workStore . route ,
640+ }
641+ )
657642 }
643+ return rscPayload
658644 }
659645
660- requestStore . stagedRendering = stageController
661- requestStore . asyncApiPromises = createAsyncApiPromisesInDev (
662- stageController ,
663- requestStore . cookies ,
664- requestStore . mutableCookies ,
665- requestStore . headers
666- )
667-
668- stream = await workUnitAsyncStorage . run (
646+ stream = await stagedRenderToReadableStreamWithoutCachesInDev (
647+ ctx ,
669648 requestStore ,
670- scheduleInSequentialTasks ,
671- ( ) => {
672- return renderToReadableStream (
673- RSCPayload ,
674- clientReferenceManifest . clientModules ,
675- {
676- onError,
677- temporaryReferences : options ?. temporaryReferences ,
678- filterStackFrame,
679- debugChannel : debugChannel ?. serverSide ,
680- environmentName,
681- }
682- )
683- } ,
684- ( ) => {
685- stageController . advanceStage ( RenderStage . Dynamic )
649+ getPayload ,
650+ clientReferenceManifest ,
651+ {
652+ onError,
653+ temporaryReferences : options ?. temporaryReferences ,
654+ filterStackFrame,
655+ debugChannel : debugChannel ?. serverSide ,
686656 }
687657 )
688658 } else {
689659 // For app dir, use the bundled version of Flight server renderer (renderToReadableStream)
690660 // which contains the subset React.
661+ const rscPayload = await workUnitAsyncStorage . run (
662+ requestStore ,
663+ generateDynamicRSCPayload ,
664+ ctx ,
665+ options
666+ )
667+
691668 stream = workUnitAsyncStorage . run (
692669 requestStore ,
693670 renderToReadableStream ,
694- RSCPayload ,
671+ rscPayload ,
695672 clientReferenceManifest . clientModules ,
696673 {
697674 onError,
@@ -707,6 +684,75 @@ async function generateDynamicFlightRenderResult(
707684 } )
708685}
709686
687+ type RenderToReadableStreamServerOptions = NonNullable <
688+ Parameters <
689+ ( typeof import ( 'react-server-dom-webpack/server.node' ) ) [ 'renderToReadableStream' ]
690+ > [ 2 ]
691+ >
692+
693+ async function stagedRenderToReadableStreamWithoutCachesInDev (
694+ ctx : AppRenderContext ,
695+ requestStore : RequestStore ,
696+ getPayload : ( requestStore : RequestStore ) => Promise < RSCPayload > ,
697+ clientReferenceManifest : NonNullable < RenderOpts [ 'clientReferenceManifest' ] > ,
698+ options : Omit < RenderToReadableStreamServerOptions , 'environmentName' >
699+ ) {
700+ const {
701+ componentMod : { renderToReadableStream } ,
702+ } = ctx
703+ // We're rendering while bypassing caches,
704+ // so we have no hope of showing a useful runtime stage.
705+ // But we still want things like `params` to show up in devtools correctly,
706+ // which relies on mechanisms we've set up for staged rendering,
707+ // so we do a 2-task version (Static -> Dynamic) instead.
708+
709+ const stageController = new StagedRenderingController ( )
710+ console . log ( 'rendering RSC payload without caches' )
711+ const environmentName = ( ) => {
712+ const currentStage = stageController . currentStage
713+ switch ( currentStage ) {
714+ case RenderStage . Static :
715+ return 'Prerender'
716+ case RenderStage . Runtime :
717+ case RenderStage . Dynamic :
718+ return 'Server'
719+ default :
720+ currentStage satisfies never
721+ throw new InvariantError ( `Invalid render stage: ${ currentStage } ` )
722+ }
723+ }
724+
725+ requestStore . stagedRendering = stageController
726+ requestStore . asyncApiPromises = createAsyncApiPromisesInDev (
727+ stageController ,
728+ requestStore . cookies ,
729+ requestStore . mutableCookies ,
730+ requestStore . headers
731+ )
732+
733+ const rscPayload = await getPayload ( requestStore )
734+
735+ return await workUnitAsyncStorage . run (
736+ requestStore ,
737+ scheduleInSequentialTasks ,
738+ ( ) => {
739+ const stream = renderToReadableStream (
740+ rscPayload ,
741+ clientReferenceManifest . clientModules ,
742+ {
743+ ...options ,
744+ environmentName,
745+ }
746+ )
747+ stageController . advanceStage ( RenderStage . Dynamic )
748+ return stream
749+ } ,
750+ ( ) => {
751+ stageController . advanceStage ( RenderStage . Dynamic )
752+ }
753+ )
754+ }
755+
710756/**
711757 * Fork of `generateDynamicFlightRenderResult` that renders using `renderWithRestartOnCacheMissInDev`
712758 * to ensure correct separation of environments Prerender/Server (for use in Cache Components)
@@ -2375,21 +2421,15 @@ async function renderToStream(
23752421 // Edge routes never prerender so we don't have a Prerender environment for anything in edge runtime
23762422 process . env . NEXT_RUNTIME !== 'edge' &&
23772423 // We only have a Prerender environment for projects opted into cacheComponents
2378- cacheComponents &&
2379- // We only do this flow if we can safely recreate the store from scratch
2380- // (which is not the case for renders after an action)
2381- createRequestStore &&
2382- // We only do this flow if we're not bypassing caches in dev using
2383- // "disable cache" in devtools or a hard refresh (cache-control: "no-store")
2384- ! isBypassingCachesInDev ( renderOpts , requestStore )
2424+ cacheComponents
23852425 ) {
23862426 type RSCPayloadWithValidation = InitialRSCPayload & {
23872427 /** Only available during cacheComponents development builds. Used for logging errors. */
23882428 _validation ?: Promise < ReactNode >
23892429 }
23902430
23912431 const [ resolveValidation , validationOutlet ] = createValidationOutlet ( )
2392-
2432+ let debugChannel : DebugChannelPair | undefined
23932433 const getPayload = async (
23942434 // eslint-disable-next-line @typescript-eslint/no-shadow
23952435 requestStore : RequestStore
@@ -2410,20 +2450,47 @@ async function renderToStream(
24102450 return payload
24112451 }
24122452
2413- const {
2414- stream : serverStream ,
2415- debugChannel,
2416- requestStore : finalRequestStore ,
2417- } = await renderWithRestartOnCacheMissInDev (
2418- ctx ,
2419- requestStore ,
2420- createRequestStore ,
2421- getPayload ,
2422- serverComponentsErrorHandler
2423- )
2453+ if (
2454+ // We only do this flow if we can safely recreate the store from scratch
2455+ // (which is not the case for renders after an action)
2456+ createRequestStore &&
2457+ // We only do this flow if we're not bypassing caches in dev using
2458+ // "disable cache" in devtools or a hard refresh (cache-control: "no-store")
2459+ ! isBypassingCachesInDev ( renderOpts , requestStore )
2460+ ) {
2461+ const {
2462+ stream : serverStream ,
2463+ debugChannel : returnedDebugChannel ,
2464+ requestStore : finalRequestStore ,
2465+ } = await renderWithRestartOnCacheMissInDev (
2466+ ctx ,
2467+ requestStore ,
2468+ createRequestStore ,
2469+ getPayload ,
2470+ serverComponentsErrorHandler
2471+ )
24242472
2425- reactServerResult = new ReactServerResult ( serverStream )
2426- requestStore = finalRequestStore
2473+ reactServerResult = new ReactServerResult ( serverStream )
2474+ requestStore = finalRequestStore
2475+ debugChannel = returnedDebugChannel
2476+ } else {
2477+ // We're either bypassing caches or we can't restart the render.
2478+ // Do a dynamic render, but with (basic) environment labels.
2479+ debugChannel = createDebugChannel && createDebugChannel ( )
2480+ const serverStream =
2481+ await stagedRenderToReadableStreamWithoutCachesInDev (
2482+ ctx ,
2483+ requestStore ,
2484+ getPayload ,
2485+ clientReferenceManifest ,
2486+ {
2487+ onError : serverComponentsErrorHandler ,
2488+ filterStackFrame,
2489+ debugChannel : debugChannel ?. serverSide ,
2490+ }
2491+ )
2492+ reactServerResult = new ReactServerResult ( serverStream )
2493+ }
24272494
24282495 if ( debugChannel && setReactDebugChannel ) {
24292496 const [ readableSsr , readableBrowser ] =
0 commit comments