diff --git a/packages/next-server/server/load-components.ts b/packages/next-server/server/load-components.ts index 542467f8361d7fa5c095fc059e55ebdb0cad74de..80789d983bed4f99ad3d7e949a6f4470cb724661 100644 --- a/packages/next-server/server/load-components.ts +++ b/packages/next-server/server/load-components.ts @@ -1,24 +1,63 @@ -import {BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, SERVERLESS_DIRECTORY} from '../lib/constants'; -import { join } from 'path'; +import { + BUILD_MANIFEST, + CLIENT_STATIC_FILES_PATH, + REACT_LOADABLE_MANIFEST, + SERVER_DIRECTORY, +} from '../lib/constants' +import { join } from 'path' -import { requirePage } from './require'; +import { requirePage } from './require' export function interopDefault(mod: any) { return mod.default || mod } -export async function loadComponents(distDir: string, buildId: string, pathname: string, serverless: boolean) { +export type LoadComponentsReturnType = { + Component: any + buildManifest?: any + reactLoadableManifest?: any + Document?: any + DocumentMiddleware?: any + App?: any, +} + +export async function loadComponents( + distDir: string, + buildId: string, + pathname: string, + serverless: boolean, +): Promise { if (serverless) { const Component = await requirePage(pathname, distDir, serverless) return { Component } } - const documentPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_document') - const appPath = join(distDir, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH, buildId, 'pages', '_app') + const documentPath = join( + distDir, + SERVER_DIRECTORY, + CLIENT_STATIC_FILES_PATH, + buildId, + 'pages', + '_document', + ) + const appPath = join( + distDir, + SERVER_DIRECTORY, + CLIENT_STATIC_FILES_PATH, + buildId, + 'pages', + '_app', + ) const DocumentMod = require(documentPath) const { middleware: DocumentMiddleware } = DocumentMod - const [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([ + const [ + buildManifest, + reactLoadableManifest, + Component, + Document, + App, + ] = await Promise.all([ require(join(distDir, BUILD_MANIFEST)), require(join(distDir, REACT_LOADABLE_MANIFEST)), interopDefault(requirePage(pathname, distDir, serverless)), diff --git a/packages/next-server/server/next-server.ts b/packages/next-server/server/next-server.ts index 2000689ed49dd976aa93fed46e71fd0bc9a0ed19..24383d7fc8ff2f4b1d57cbe5382cccf315b7fe34 100644 --- a/packages/next-server/server/next-server.ts +++ b/packages/next-server/server/next-server.ts @@ -21,8 +21,12 @@ import { PAGES_MANIFEST, } from '../lib/constants' import * as envConfig from '../lib/runtime-config' -import { loadComponents, interopDefault } from './load-components' -import { getPagePath } from './require'; +import { + loadComponents, + interopDefault, + LoadComponentsReturnType, +} from './load-components' +import { getPagePath } from './require' type NextConfig = any @@ -47,8 +51,8 @@ export default class Server { buildId: string generateEtags: boolean runtimeConfig?: { [key: string]: any } - assetPrefix?: string, - autoExport: boolean, + assetPrefix?: string + autoExport: boolean dev?: boolean, } router: Router @@ -237,7 +241,11 @@ export default class Server { * @param res http response * @param pathname path of request */ - private async handleApiRequest(req: IncomingMessage, res: ServerResponse, pathname: string) { + private async handleApiRequest( + req: IncomingMessage, + res: ServerResponse, + pathname: string, + ) { const resolverFunction = await this.resolveApiRequest(pathname) if (resolverFunction === null) { res.statusCode = 404 @@ -254,7 +262,11 @@ export default class Server { * @param pathname path of request */ private resolveApiRequest(pathname: string) { - return getPagePath(pathname, this.distDir, this.nextConfig.target === 'serverless') + return getPagePath( + pathname, + this.distDir, + this.nextConfig.target === 'serverless', + ) } private generatePublicRoutes(): Route[] { @@ -333,7 +345,11 @@ export default class Server { } const html = await this.renderToHTML(req, res, pathname, query, { - dataOnly: this.renderOpts.ampBindInitData && Boolean(query.dataOnly) || (req.headers && (req.headers.accept || '').indexOf('application/amp.bind+json') !== -1), + dataOnly: + (this.renderOpts.ampBindInitData && Boolean(query.dataOnly)) || + (req.headers && + (req.headers.accept || '').indexOf('application/amp.bind+json') !== + -1), }) // Request was ended by the user if (html === null) { @@ -343,32 +359,54 @@ export default class Server { return this.sendHTML(req, res, html) } - private async renderToHTMLWithComponents( - req: IncomingMessage, - res: ServerResponse, + private async findPageComponents( pathname: string, query: ParsedUrlQuery = {}, - opts: any, ) { - const serverless = !this.renderOpts.dev && this.nextConfig.target === 'serverless' + const serverless = + !this.renderOpts.dev && this.nextConfig.target === 'serverless' // try serving a static AMP version first if (query.amp) { try { - const result = await loadComponents(this.distDir, this.buildId, (pathname === '/' ? '/index' : pathname) + '.amp', serverless) - if (typeof result.Component === 'string') return result.Component + return await loadComponents( + this.distDir, + this.buildId, + (pathname === '/' ? '/index' : pathname) + '.amp', + serverless, + ) } catch (err) { if (err.code !== 'ENOENT') throw err } } - const result = await loadComponents(this.distDir, this.buildId, pathname, serverless) + return await loadComponents( + this.distDir, + this.buildId, + pathname, + serverless, + ) + } + + private async renderToHTMLWithComponents( + req: IncomingMessage, + res: ServerResponse, + pathname: string, + query: ParsedUrlQuery = {}, + result: LoadComponentsReturnType, + opts: any, + ) { // handle static page - if (typeof result.Component === 'string') return result.Component + if (typeof result.Component === 'string') { + return result.Component + } + // handle serverless - if (typeof result.Component === 'object' && + if ( + typeof result.Component === 'object' && typeof result.Component.renderReqToHTML === 'function' ) { return result.Component.renderReqToHTML(req, res) } + return renderToHTML(req, res, pathname, query, { ...result, ...opts }) } @@ -377,19 +415,25 @@ export default class Server { res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}, - { amphtml, dataOnly, hasAmp }: { - amphtml?: boolean, - hasAmp?: boolean, + { + amphtml, + dataOnly, + hasAmp, + }: { + amphtml?: boolean + hasAmp?: boolean dataOnly?: boolean, } = {}, ): Promise { try { // To make sure the try/catch is executed + const result = await this.findPageComponents(pathname, query) const html = await this.renderToHTMLWithComponents( req, res, pathname, query, + result, { ...this.renderOpts, amphtml, hasAmp, dataOnly }, ) return html @@ -430,7 +474,8 @@ export default class Server { _pathname: string, query: ParsedUrlQuery = {}, ) { - return this.renderToHTMLWithComponents(req, res, '/_error', query, { + const result = await this.findPageComponents('/_error', query) + return this.renderToHTMLWithComponents(req, res, '/_error', query, result, { ...this.renderOpts, err, })