Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pages Error Route Module Rendering #51374

Merged
merged 9 commits into from Jun 22, 2023
Expand Up @@ -116,17 +116,16 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
${
isAppDir
? `
import { renderToHTMLOrFlight as appRenderToHTML } from 'next/dist/esm/server/app-render/app-render'
import { renderToHTMLOrFlight as renderToHTML } from 'next/dist/esm/server/app-render/app-render'
import * as pageMod from ${JSON.stringify(pageModPath)}
const Document = null
const pagesRenderToHTML = null
const appMod = null
const errorMod = null
const error500Mod = null
`
: `
import Document from ${stringifiedDocumentPath}
import { renderToHTML as pagesRenderToHTML } from 'next/dist/esm/server/render'
import { renderToHTML } from 'next/dist/esm/server/render'
import * as pageMod from ${stringifiedPagePath}
import * as appMod from ${stringifiedAppPath}
import * as errorMod from ${stringifiedErrorPath}
Expand All @@ -135,7 +134,6 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
? `import * as error500Mod from ${stringified500Path}`
: `const error500Mod = null`
}
const appRenderToHTML = null
`
}

Expand Down Expand Up @@ -171,8 +169,7 @@ const edgeSSRLoader: webpack.LoaderDefinitionFunction<EdgeSSRLoaderQuery> =
buildManifest,
isAppPath: ${!!isAppDir},
prerenderManifest,
appRenderToHTML,
pagesRenderToHTML,
renderToHTML,
reactLoadableManifest,
clientReferenceManifest: ${isServerComponent} ? rscManifest : null,
serverActionsManifest: ${isServerComponent} ? rscServerManifest : null,
Expand Down
Expand Up @@ -27,8 +27,7 @@ export function getRender({
buildManifest,
prerenderManifest,
reactLoadableManifest,
appRenderToHTML,
pagesRenderToHTML,
renderToHTML,
clientReferenceManifest,
subresourceIntegrityManifest,
serverActionsManifest,
Expand All @@ -44,8 +43,7 @@ export function getRender({
pageMod: any
errorMod: any
error500Mod: any
appRenderToHTML: any
pagesRenderToHTML: any
renderToHTML: any
Document: DocumentType
buildManifest: BuildManifest
prerenderManifest: PrerenderManifest
Expand Down Expand Up @@ -88,8 +86,7 @@ export function getRender({
clientReferenceManifest,
serverActionsManifest,
},
appRenderToHTML,
pagesRenderToHTML,
renderToHTML,
incrementalCacheHandler,
loadComponent: async (pathname) => {
if (pathname === page) {
Expand Down Expand Up @@ -137,12 +134,15 @@ export function getRender({
},
},
})
const requestHandler = server.getRequestHandler()

const handler = server.getRequestHandler()

return async function render(request: Request) {
const extendedReq = new WebNextRequest(request)
const extendedRes = new WebNextResponse()
requestHandler(extendedReq, extendedRes)

handler(extendedReq, extendedRes)

return await extendedRes.toResponse()
}
}
31 changes: 20 additions & 11 deletions packages/next/src/server/base-server.ts
Expand Up @@ -61,7 +61,11 @@ import * as Log from '../build/output/log'
import escapePathDelimiters from '../shared/lib/router/utils/escape-path-delimiters'
import { getUtils } from './server-utils'
import isError, { getProperError } from '../lib/is-error'
import { addRequestMeta, getRequestMeta } from './request-meta'
import {
addRequestMeta,
getRequestMeta,
removeRequestMeta,
} from './request-meta'

import { ImageConfigComplete } from '../shared/lib/image-config'
import { removePathPrefix } from '../shared/lib/router/utils/remove-path-prefix'
Expand Down Expand Up @@ -1670,11 +1674,8 @@ export default abstract class Server<ServerOptions extends Options = Options> {
// served by the server.
let result: RenderResult

// We want to use the match when we're not trying to render an error page.
const match =
pathname !== '/_error' && !is404Page && !is500Page
? getRequestMeta(req, '_nextMatch')
: undefined
// Get the match for the page if it exists.
const match = getRequestMeta(req, '_nextMatch')

if (
match &&
Expand Down Expand Up @@ -1716,12 +1717,9 @@ export default abstract class Server<ServerOptions extends Options = Options> {
if (!headers['content-type'] && blob.type) {
headers['content-type'] = blob.type
}
let revalidate: number | false | undefined =
context.staticGenerationContext.store?.revalidate

if (typeof revalidate == 'undefined') {
revalidate = false
}
const revalidate =
context.staticGenerationContext.store?.revalidate ?? false

// Create the cache entry for the response.
const cacheEntry: ResponseCacheEntry = {
Expand Down Expand Up @@ -2540,6 +2538,17 @@ export default abstract class Server<ServerOptions extends Options = Options> {
)
}

// If the page has a route module, use it for the new match. If it doesn't
// have a route module, remove the match.
if (result.components.ComponentMod.routeModule) {
addRequestMeta(ctx.req, '_nextMatch', {
definition: result.components.ComponentMod.routeModule.definition,
params: undefined,
})
} else {
removeRequestMeta(ctx.req, '_nextMatch')
}

try {
return await this.renderToResponseWithComponents(
{
Expand Down
41 changes: 40 additions & 1 deletion packages/next/src/server/request-meta.ts
Expand Up @@ -42,6 +42,14 @@ export interface RequestMeta {
_nextMinimalMode?: boolean
}

/**
* Gets the request metadata. If no key is provided, the entire metadata object
* is returned.
*
* @param req the request to get the metadata from
* @param key the key to get from the metadata (optional)
* @returns the value for the key or the entire metadata object
*/
export function getRequestMeta(
req: NextIncomingMessage,
key?: undefined
Expand All @@ -58,11 +66,26 @@ export function getRequestMeta<K extends keyof RequestMeta>(
return typeof key === 'string' ? meta[key] : meta
}

/**
* Sets the request metadata.
*
* @param req the request to set the metadata on
* @param meta the metadata to set
* @returns the mutated request metadata
*/
export function setRequestMeta(req: NextIncomingMessage, meta: RequestMeta) {
req[NEXT_REQUEST_META] = meta
return getRequestMeta(req)
return meta
}

/**
* Adds a value to the request metadata.
*
* @param request the request to mutate
* @param key the key to set
* @param value the value to set
* @returns the mutated request metadata
*/
export function addRequestMeta<K extends keyof RequestMeta>(
request: NextIncomingMessage,
key: K,
Expand All @@ -73,6 +96,22 @@ export function addRequestMeta<K extends keyof RequestMeta>(
return setRequestMeta(request, meta)
}

/**
* Removes a key from the request metadata.
*
* @param request the request to mutate
* @param key the key to remove
* @returns the mutated request metadata
*/
export function removeRequestMeta<K extends keyof RequestMeta>(
request: NextIncomingMessage,
key: K
) {
const meta = getRequestMeta(request)
delete meta[key]
return setRequestMeta(request, meta)
}

type NextQueryMetadata = {
__nextNotFoundSrcPage?: string
__nextDefaultLocale?: string
Expand Down
38 changes: 17 additions & 21 deletions packages/next/src/server/web-server.ts
Expand Up @@ -35,8 +35,9 @@ interface WebServerOptions extends Options {
) => Promise<LoadComponentsReturnType | null>
extendRenderOpts: Partial<BaseServer['renderOpts']> &
Pick<BaseServer['renderOpts'], 'buildId'>
pagesRenderToHTML?: typeof import('./render').renderToHTML
appRenderToHTML?: typeof import('./app-render/app-render').renderToHTMLOrFlight
renderToHTML:
| typeof import('./render').renderToHTML
| typeof import('./app-render/app-render').renderToHTMLOrFlight
incrementalCacheHandler?: any
prerenderManifest: PrerenderManifest | undefined
}
Expand Down Expand Up @@ -362,32 +363,27 @@ export default class NextWebServer extends BaseServer<WebServerOptions> {
return false
}

protected async renderHTML(
protected renderHTML(
req: WebNextRequest,
res: WebNextResponse,
pathname: string,
query: NextParsedUrlQuery,
renderOpts: RenderOpts
): Promise<RenderResult> {
const { pagesRenderToHTML, appRenderToHTML } =
this.serverOptions.webServerConfig
const curRenderToHTML = pagesRenderToHTML || appRenderToHTML

if (curRenderToHTML) {
return await curRenderToHTML(
req as any,
res as any,
pathname,
query,
Object.assign(renderOpts, {
disableOptimizedLoading: true,
runtime: 'experimental-edge',
})
)
} else {
throw new Error(`Invariant: curRenderToHTML is missing`)
}
const { renderToHTML } = this.serverOptions.webServerConfig

return renderToHTML(
req as any,
res as any,
pathname,
query,
Object.assign(renderOpts, {
disableOptimizedLoading: true,
runtime: 'experimental-edge',
})
)
}

protected async sendRenderResult(
_req: WebNextRequest,
res: WebNextResponse,
Expand Down