Skip to content

Commit 2df46b4

Browse files
committed
Implement handler interface for app-page
1 parent 48ac8ce commit 2df46b4

File tree

15 files changed

+711
-114
lines changed

15 files changed

+711
-114
lines changed

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

Lines changed: 467 additions & 2 deletions
Large diffs are not rendered by default.

packages/next/src/build/templates/pages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ export async function handler(
245245
locale,
246246
locales,
247247
defaultLocale,
248+
setIsrStatus: routerServerContext?.setIsrStatus,
248249

249250
isNextDataRequest:
250251
isNextDataRequest && (hasServerProps || hasStaticProps),

packages/next/src/server/base-server.ts

Lines changed: 87 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,56 +2779,59 @@ export default abstract class Server<
27792779
return null
27802780
}
27812781

2782-
if (isPagesRouteModule(routeModule)) {
2783-
const request = isNodeNextRequest(req) ? req.originalRequest : req
2782+
const request = isNodeNextRequest(req) ? req.originalRequest : req
2783+
const response = isNodeNextResponse(res) ? res.originalResponse : res
27842784

2785-
const response = isNodeNextResponse(res)
2786-
? res.originalResponse
2787-
: res
2788-
2789-
if (
2790-
components.ComponentMod.handler &&
2791-
process.env.NEXT_RUNTIME !== 'edge'
2792-
) {
2793-
const parsedInitUrl = parseUrl(
2794-
getRequestMeta(req, 'initURL') || req.url
2795-
)
2796-
request.url =
2797-
req.url = `${parsedInitUrl.pathname}${parsedInitUrl.search || ''}`
2798-
2799-
// propagate the request context for dev
2800-
setRequestMeta(request, getRequestMeta(req))
2801-
addRequestMeta(request, 'projectDir', this.dir)
2802-
addRequestMeta(request, 'isIsrFallback', pagesFallback)
2803-
addRequestMeta(request, 'query', query)
2804-
addRequestMeta(request, 'params', opts.params)
2805-
addRequestMeta(
2806-
request,
2807-
'ampValidator',
2808-
this.renderOpts.ampValidator
2809-
)
2785+
if (
2786+
components.ComponentMod.handler &&
2787+
process.env.NEXT_RUNTIME !== 'edge'
2788+
) {
2789+
const parsedInitUrl = parseUrl(
2790+
getRequestMeta(req, 'initURL') || req.url
2791+
)
2792+
request.url =
2793+
req.url = `${parsedInitUrl.pathname}${parsedInitUrl.search || ''}`
2794+
2795+
// propagate the request context for dev
2796+
setRequestMeta(request, getRequestMeta(req))
2797+
addRequestMeta(request, 'postponed', postponed)
2798+
addRequestMeta(request, 'projectDir', this.dir)
2799+
addRequestMeta(request, 'isIsrFallback', pagesFallback)
2800+
addRequestMeta(
2801+
request,
2802+
'renderFallbackShell',
2803+
Boolean(fallbackRouteParams)
2804+
)
2805+
addRequestMeta(request, 'query', query)
2806+
addRequestMeta(request, 'params', opts.params)
2807+
addRequestMeta(
2808+
request,
2809+
'ampValidator',
2810+
this.renderOpts.ampValidator
2811+
)
28102812

2811-
if (renderOpts.err) {
2812-
addRequestMeta(request, 'invokeError', renderOpts.err)
2813+
if (renderOpts.err) {
2814+
addRequestMeta(request, 'invokeError', renderOpts.err)
2815+
}
2816+
const handler: (
2817+
req: ServerRequest | IncomingMessage,
2818+
res: ServerResponse | HTTPServerResponse,
2819+
ctx: {
2820+
waitUntil: ReturnType<Server['getWaitUntil']>
28132821
}
2814-
const handler: (
2815-
req: ServerRequest | IncomingMessage,
2816-
res: ServerResponse | HTTPServerResponse,
2817-
ctx: {
2818-
waitUntil: ReturnType<Server['getWaitUntil']>
2819-
}
2820-
) => Promise<RenderResult> = components.ComponentMod.handler
2822+
) => Promise<RenderResult> = components.ComponentMod.handler
28212823

2822-
result = await handler(request, response, {
2823-
waitUntil: this.getWaitUntil(),
2824-
})
2824+
result = await handler(request, response, {
2825+
waitUntil: this.getWaitUntil(),
2826+
})
28252827

2826-
if (!result) {
2827-
throw new Error(
2828-
`Invariant: missing result from invoking ${pathname} handler`
2829-
)
2830-
}
2831-
} else {
2828+
if (!result) {
2829+
throw new Error(
2830+
`Invariant: missing result from invoking ${pathname} handler`
2831+
)
2832+
}
2833+
} else {
2834+
if (isPagesRouteModule(routeModule)) {
28322835
// Due to the way we pass data by mutating `renderOpts`, we can't extend
28332836
// the object here but only updating its `clientReferenceManifest` and
28342837
// `nextFontManifest` properties.
@@ -2875,48 +2878,48 @@ export default abstract class Server<
28752878
})
28762879
throw err
28772880
}
2878-
}
2879-
} else {
2880-
const module = components.routeModule as AppPageRouteModule
2881-
2882-
// Due to the way we pass data by mutating `renderOpts`, we can't extend the
2883-
// object here but only updating its `nextFontManifest` field.
2884-
// https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952
2885-
renderOpts.nextFontManifest = this.nextFontManifest
2886-
2887-
const context: AppPageRouteHandlerContext = {
2888-
page: is404Page ? '/404' : pathname,
2889-
params: opts.params,
2890-
query,
2891-
fallbackRouteParams,
2892-
renderOpts,
2893-
serverComponentsHmrCache: this.getServerComponentsHmrCache(),
2894-
sharedContext: {
2895-
buildId: this.buildId,
2896-
},
2897-
}
2881+
} else {
2882+
const module = components.routeModule as AppPageRouteModule
28982883

2899-
// TODO: adapt for putting the RDC inside the postponed data
2900-
// If we're in dev, and this isn't a prefetch or a server action,
2901-
// we should seed the resume data cache.
2902-
if (
2903-
this.nextConfig.experimental.dynamicIO &&
2904-
this.renderOpts.dev &&
2905-
!isPrefetchRSCRequest &&
2906-
!isPossibleServerAction
2907-
) {
2908-
const warmup = await module.warmup(req, res, context)
2884+
// Due to the way we pass data by mutating `renderOpts`, we can't extend the
2885+
// object here but only updating its `nextFontManifest` field.
2886+
// https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952
2887+
renderOpts.nextFontManifest = this.nextFontManifest
29092888

2910-
// If the warmup is successful, we should use the resume data
2911-
// cache from the warmup.
2912-
if (warmup.metadata.devRenderResumeDataCache) {
2913-
renderOpts.devRenderResumeDataCache =
2914-
warmup.metadata.devRenderResumeDataCache
2889+
const context: AppPageRouteHandlerContext = {
2890+
page: is404Page ? '/404' : pathname,
2891+
params: opts.params,
2892+
query,
2893+
fallbackRouteParams,
2894+
renderOpts,
2895+
serverComponentsHmrCache: this.getServerComponentsHmrCache(),
2896+
sharedContext: {
2897+
buildId: this.buildId,
2898+
},
29152899
}
2916-
}
29172900

2918-
// Call the built-in render method on the module.
2919-
result = await module.render(req, res, context)
2901+
// TODO: adapt for putting the RDC inside the postponed data
2902+
// If we're in dev, and this isn't a prefetch or a server action,
2903+
// we should seed the resume data cache.
2904+
if (
2905+
this.nextConfig.experimental.dynamicIO &&
2906+
this.renderOpts.dev &&
2907+
!isPrefetchRSCRequest &&
2908+
!isPossibleServerAction
2909+
) {
2910+
const warmup = await module.warmup(req, res, context)
2911+
2912+
// If the warmup is successful, we should use the resume data
2913+
// cache from the warmup.
2914+
if (warmup.metadata.devRenderResumeDataCache) {
2915+
renderOpts.devRenderResumeDataCache =
2916+
warmup.metadata.devRenderResumeDataCache
2917+
}
2918+
}
2919+
2920+
// Call the built-in render method on the module.
2921+
result = await module.render(req, res, context)
2922+
}
29202923
}
29212924
} else {
29222925
throw new Error('Invariant: Unknown route module type')
@@ -3205,7 +3208,7 @@ export default abstract class Server<
32053208
postponed: undefined,
32063209
pagesFallback: undefined,
32073210
fallbackRouteParams:
3208-
// If we're in production of we're debugging the fallback
3211+
// If we're in production or we're debugging the fallback
32093212
// shell then we should postpone when dynamic params are
32103213
// accessed.
32113214
isProduction || isDebugFallbackShell

packages/next/src/server/lib/router-server.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,11 @@ export async function initialize(opts: {
676676
nextConfig: config,
677677
hostname: handlers.server.hostname,
678678
revalidate: handlers.server.revalidate.bind(handlers.server),
679+
experimentalTestProxy: renderServerOpts.experimentalTestProxy,
680+
logErrorWithOriginalStack: opts.dev
681+
? handlers.server.logErrorWithOriginalStack.bind(handlers.server)
682+
: (err: unknown) => Log.error(err),
683+
setIsrStatus: devBundlerService?.setIsrStatus.bind(devBundlerService),
679684
}
680685

681686
const logError = async (

packages/next/src/server/lib/router-utils/block-cross-site.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Duplex } from 'stream'
2-
import type { IncomingMessage, ServerResponse } from 'webpack-dev-server'
2+
import type { IncomingMessage, ServerResponse } from 'node:http'
33
import { parseUrl } from '../../../lib/url'
44
import { warnOnce } from '../../../build/output/log'
55
import { isCsrfOriginAllowed } from '../../app-render/csrf-protection'

packages/next/src/server/lib/router-utils/router-server-context.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export type RouterServerContext = Record<
2525
nextConfig?: NextConfigComplete
2626
// whether running in custom server mode
2727
isCustomServer?: boolean
28+
// whether test proxy is enabled
29+
experimentalTestProxy?: boolean
30+
// allow dev server to log with original stack
31+
logErrorWithOriginalStack?: (err: unknown, type: string) => void
32+
// allow setting ISR status in dev
33+
setIsrStatus?: (key: string, value: boolean | null) => void
2834
}
2935
>
3036

packages/next/src/server/lib/streaming-metadata.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export function shouldServeStreamingMetadata(
2020

2121
// When the request UA is a html-limited bot, we should do a dynamic render.
2222
// In this case, postpone state is not sent.
23-
export function isHtmlBotRequest(req: BaseNextRequest): boolean {
23+
export function isHtmlBotRequest(req: {
24+
headers: BaseNextRequest['headers']
25+
}): boolean {
2426
const ua = req.headers['user-agent'] || ''
2527
const botType = getBotType(ua)
2628

packages/next/src/server/load-manifest.external.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export function loadManifestFromRelativePath<T extends object>({
115115
cache,
116116
skipParse,
117117
handleMissing,
118+
useEval,
118119
}: {
119120
projectDir: string
120121
distDir: string
@@ -123,14 +124,19 @@ export function loadManifestFromRelativePath<T extends object>({
123124
cache?: Map<string, unknown>
124125
skipParse?: boolean
125126
handleMissing?: boolean
127+
useEval?: boolean
126128
}): DeepReadonly<T> {
127129
try {
128-
return loadManifest<T>(
129-
join(/* turbopackIgnore: true */ projectDir, distDir, manifest),
130-
shouldCache,
131-
cache,
132-
skipParse
130+
const manifestPath = join(
131+
/* turbopackIgnore: true */ projectDir,
132+
distDir,
133+
manifest
133134
)
135+
136+
if (useEval) {
137+
return evalManifest<T>(manifestPath, shouldCache, cache)
138+
}
139+
return loadManifest<T>(manifestPath, shouldCache, cache, skipParse)
134140
} catch (err) {
135141
if (handleMissing) {
136142
// TODO: should this be undefined

packages/next/src/server/next.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ interface NextWrapperServer {
8686
...args: Parameters<NextNodeServer['revalidate']>
8787
): ReturnType<NextNodeServer['revalidate']>
8888

89+
logErrorWithOriginalStack(err: unknown, type: string): void
90+
8991
render(
9092
...args: Parameters<NextNodeServer['render']>
9193
): ReturnType<NextNodeServer['render']>
@@ -165,6 +167,14 @@ export class NextServer implements NextWrapperServer {
165167
}
166168
}
167169

170+
async logErrorWithOriginalStack(err: unknown, type: string) {
171+
const server = await this.getServer()
172+
// this is only available on dev server
173+
if ((server as any).logErrorWithOriginalStack) {
174+
return (server as any).logErrorWithOriginalStack(err, type)
175+
}
176+
}
177+
168178
async revalidate(...args: Parameters<NextWrapperServer['revalidate']>) {
169179
const server = await this.getServer()
170180
return server.revalidate(...args)
@@ -452,6 +462,10 @@ class NextCustomServer implements NextWrapperServer {
452462
this.server.logError(...args)
453463
}
454464

465+
logErrorWithOriginalStack(err: unknown, type: string) {
466+
return this.server.logErrorWithOriginalStack(err, type)
467+
}
468+
455469
async revalidate(...args: Parameters<NextWrapperServer['revalidate']>) {
456470
return this.server.revalidate(...args)
457471
}

packages/next/src/server/route-modules/app-page/module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ export class AppPageRouteModule extends RouteModule<
5858
AppPageRouteDefinition,
5959
AppPageUserlandModule
6060
> {
61+
constructor(
62+
options: RouteModuleOptions<AppPageRouteDefinition, AppPageUserlandModule>
63+
) {
64+
super(options)
65+
this.isAppRouter = true
66+
}
67+
6168
public render(
6269
req: BaseNextRequest,
6370
res: BaseNextResponse,

0 commit comments

Comments
 (0)