Skip to content

Commit 3e085dd

Browse files
gnoffunstubbable
authored andcommitted
Introduce prerenderAsyncStorage and refactor app-render
In preparation for a number of new features and rendering modes this PR dramatically refactors app-render and related files to make future contributions easier to land. Historically we have used AsyncLocalStorage to scope state that is contextual for an entire page export (RSC > SSR > error handling etc...) however it can be useful and is generally a good practice to push the ALS scoping deeper into the render when possible. One case is for tracking dynamic access. in this PR we introduce prerenderAsyncStorage which is wrapped around individual render calls into React. If there is a store during a render then we can infer we are in an App Router prerender. Longer term we should consider combinging the requestStorage and staticGenerationStorage since these are scoped at the same level and the name of staticGenerationStorage is misleading because it is in scope even when there is no static generation happening. In addition to storage changes the code paths for app-render are reorganized to use fewer closures and to consolidate argument passing. Additionally we now treat render (dynamic request handling) and prerender (build and revalidate handling) in separate code paths. This allows for clearer login within each pathway and fewer repeated conditionals. There are still a number of things to further clean up like how we track metadata. One idea is to have the render/prerender functions simply return a RenderResult. Another area is error handling. There is currently a bug where ssr only errors won't fail a build the same way an RSC error would. To keep this PR clean I've reproduced this behavior even if it is buggy so we can land a fix stacked on top
1 parent abf12f8 commit 3e085dd

21 files changed

+1487
-1133
lines changed

packages/next/src/build/templates/app-route.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ const { requestAsyncStorage, staticGenerationAsyncStorage, serverHooks } =
3636
routeModule
3737

3838
function patchFetch() {
39-
return _patchFetch({ staticGenerationAsyncStorage, requestAsyncStorage })
39+
return _patchFetch({
40+
staticGenerationAsyncStorage,
41+
requestAsyncStorage,
42+
})
4043
}
4144

4245
export {

packages/next/src/client/components/static-generation-async-storage.external.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { IncrementalCache } from '../../server/lib/incremental-cache'
33
import type { DynamicServerError } from './hooks-server-context'
44
import type { FetchMetrics } from '../../server/base-http'
55
import type { Revalidate } from '../../server/lib/revalidate'
6-
import type { PrerenderState } from '../../server/app-render/dynamic-rendering'
76

87
// Share the instance module in the next-shared layer
98
import { staticGenerationAsyncStorage } from './static-generation-async-storage-instance' with { 'turbopack-transition': 'next-shared' }
@@ -28,9 +27,6 @@ export interface StaticGenerationStore {
2827
readonly isRevalidate?: boolean
2928
readonly isUnstableCacheCallback?: boolean
3029

31-
// When this exists (is not null) it means we are in a Prerender
32-
prerenderState: null | PrerenderState
33-
3430
forceDynamic?: boolean
3531
fetchCache?:
3632
| 'only-cache'

packages/next/src/server/app-render/action-handler.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ export async function handleAction({
539539

540540
return {
541541
type: 'done',
542-
result: await generateFlight(ctx, {
542+
result: await generateFlight(req, ctx, {
543543
actionResult: promise,
544544
// if the page was not revalidated, we can skip the rendering the flight tree
545545
skipFlight: !staticGenerationStore.pathWasRevalidated,
@@ -817,7 +817,7 @@ export async function handleAction({
817817
requestStore,
818818
})
819819

820-
actionResult = await generateFlight(ctx, {
820+
actionResult = await generateFlight(req, ctx, {
821821
actionResult: Promise.resolve(returnVal),
822822
// if the page was not revalidated, or if the action was forwarded from another worker, we can skip the rendering the flight tree
823823
skipFlight:
@@ -895,7 +895,7 @@ export async function handleAction({
895895
}
896896
return {
897897
type: 'done',
898-
result: await generateFlight(ctx, {
898+
result: await generateFlight(req, ctx, {
899899
skipFlight: false,
900900
actionResult: promise,
901901
asNotFound: true,
@@ -928,7 +928,7 @@ export async function handleAction({
928928

929929
return {
930930
type: 'done',
931-
result: await generateFlight(ctx, {
931+
result: await generateFlight(req, ctx, {
932932
actionResult: promise,
933933
// if the page was not revalidated, or if the action was forwarded from another worker, we can skip the rendering the flight tree
934934
skipFlight:

0 commit comments

Comments
 (0)