mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Move out requires from renderToHTML (#5915)
This brings us one step closer to outputting serverless functions as renderToHTML now renders the passed components, which allows us to bundle the renderToHTML function together with statically imported components in webpack.
This commit is contained in:
parent
cd0a1767f4
commit
32451e979e
|
@ -5,7 +5,7 @@ export type ManifestItem = {
|
|||
publicPath: string
|
||||
}
|
||||
|
||||
type Manifest = {[moduleId: string]: ManifestItem[]}
|
||||
export type Manifest = {[moduleId: string]: ManifestItem[]}
|
||||
|
||||
type DynamicImportBundles = Set<ManifestItem>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {normalizePagePath} from './require'
|
||||
|
||||
type BuildManifest = {
|
||||
export type BuildManifest = {
|
||||
devFiles: string[],
|
||||
pages: {
|
||||
[page: string]: string[]
|
||||
}
|
||||
|
|
21
packages/next-server/server/load-components.ts
Normal file
21
packages/next-server/server/load-components.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import {join} from 'path'
|
||||
import {CLIENT_STATIC_FILES_PATH, BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants'
|
||||
import {requirePage} from './require'
|
||||
|
||||
function interopDefault (mod: any) {
|
||||
return mod.default || mod
|
||||
}
|
||||
|
||||
export async function loadComponents (distDir: string, buildId: string, pathname: string) {
|
||||
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')
|
||||
let [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
|
||||
require(join(distDir, BUILD_MANIFEST)),
|
||||
require(join(distDir, REACT_LOADABLE_MANIFEST)),
|
||||
interopDefault(requirePage(pathname, distDir)),
|
||||
interopDefault(require(documentPath)),
|
||||
interopDefault(require(appPath))
|
||||
])
|
||||
|
||||
return {buildManifest, reactLoadableManifest, Component, Document, App}
|
||||
}
|
|
@ -10,9 +10,10 @@ import {serveStatic} from './serve-static'
|
|||
import Router, {route, Route} from './router'
|
||||
import { isInternalUrl, isBlockedPage } from './utils'
|
||||
import loadConfig from 'next-server/next-config'
|
||||
import {PHASE_PRODUCTION_SERVER, BUILD_ID_FILE, CLIENT_STATIC_FILES_PATH, CLIENT_STATIC_FILES_RUNTIME} from 'next-server/constants'
|
||||
import {PHASE_PRODUCTION_SERVER, BUILD_ID_FILE, CLIENT_STATIC_FILES_PATH, CLIENT_STATIC_FILES_RUNTIME, BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY} from 'next-server/constants'
|
||||
import * as asset from '../lib/asset'
|
||||
import * as envConfig from '../lib/runtime-config'
|
||||
import {loadComponents} from './load-components'
|
||||
|
||||
type NextConfig = any
|
||||
|
||||
|
@ -79,6 +80,11 @@ export default class Server {
|
|||
return PHASE_PRODUCTION_SERVER
|
||||
}
|
||||
|
||||
private logError(...args: any): void {
|
||||
if(this.quiet) return
|
||||
console.error(...args)
|
||||
}
|
||||
|
||||
private handleRequest (req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> {
|
||||
// Parse url if parsedUrl not provided
|
||||
if (!parsedUrl || typeof parsedUrl !== 'object') {
|
||||
|
@ -94,7 +100,7 @@ export default class Server {
|
|||
res.statusCode = 200
|
||||
return this.run(req, res, parsedUrl)
|
||||
.catch((err) => {
|
||||
if (!this.quiet) console.error(err)
|
||||
this.logError(err)
|
||||
res.statusCode = 500
|
||||
res.end('Internal Server Error')
|
||||
})
|
||||
|
@ -224,17 +230,22 @@ export default class Server {
|
|||
return this.sendHTML(req, res, html)
|
||||
}
|
||||
|
||||
private async renderToHTMLWithComponents(req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}, opts: any) {
|
||||
const result = await loadComponents(this.distDir, this.buildId, pathname)
|
||||
return renderToHTML(req, res, pathname, query, {...result, ...opts})
|
||||
}
|
||||
|
||||
public async renderToHTML (req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery = {}): Promise<string|null> {
|
||||
try {
|
||||
// To make sure the try/catch is executed
|
||||
const html = await renderToHTML(req, res, pathname, query, this.renderOpts)
|
||||
const html = await this.renderToHTMLWithComponents(req, res, pathname, query, this.renderOpts)
|
||||
return html
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
res.statusCode = 404
|
||||
return this.renderErrorToHTML(null, req, res, pathname, query)
|
||||
} else {
|
||||
if (!this.quiet) console.error(err)
|
||||
this.logError(err)
|
||||
res.statusCode = 500
|
||||
return this.renderErrorToHTML(err, req, res, pathname, query)
|
||||
}
|
||||
|
@ -251,7 +262,7 @@ export default class Server {
|
|||
}
|
||||
|
||||
public async renderErrorToHTML (err: Error|null, req: IncomingMessage, res: ServerResponse, _pathname: string, query: ParsedUrlQuery = {}) {
|
||||
return renderToHTML(req, res, '/_error', query, {...this.renderOpts, err})
|
||||
return this.renderToHTMLWithComponents(req, res, '/_error', query, {...this.renderOpts, err})
|
||||
}
|
||||
|
||||
public async render404 (req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery): Promise<void> {
|
||||
|
|
|
@ -3,15 +3,14 @@ import { ParsedUrlQuery } from 'querystring'
|
|||
import { join } from 'path'
|
||||
import React from 'react'
|
||||
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
|
||||
import {requirePage} from './require'
|
||||
import Router from '../lib/router/router'
|
||||
import { loadGetInitialProps, isResSent } from '../lib/utils'
|
||||
import Head, { defaultHead } from '../lib/head'
|
||||
import Loadable from '../lib/loadable'
|
||||
import LoadableCapture from '../lib/loadable-capture'
|
||||
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, SERVER_DIRECTORY, CLIENT_STATIC_FILES_PATH } from 'next-server/constants'
|
||||
import {getDynamicImportBundles, ManifestItem} from './get-dynamic-import-bundles'
|
||||
import {getPageFiles} from './get-page-files'
|
||||
import { SERVER_DIRECTORY } from 'next-server/constants'
|
||||
import {getDynamicImportBundles, Manifest as ReactLoadableManifest, ManifestItem} from './get-dynamic-import-bundles'
|
||||
import {getPageFiles, BuildManifest} from './get-page-files'
|
||||
|
||||
type Enhancer = (Component: React.ComponentType) => React.ComponentType
|
||||
type ComponentsEnhancer = {enhanceApp?: Enhancer, enhanceComponent?: Enhancer}|Enhancer
|
||||
|
@ -34,10 +33,6 @@ function enhanceComponents(options: ComponentsEnhancer, App: React.ComponentType
|
|||
}
|
||||
}
|
||||
|
||||
function interoptDefault(mod: any) {
|
||||
return mod.default || mod
|
||||
}
|
||||
|
||||
function render(renderElementToString: (element: React.ReactElement<any>) => string, element: React.ReactElement<any>): {html: string, head: any} {
|
||||
let html
|
||||
let head
|
||||
|
@ -59,7 +54,13 @@ type RenderOpts = {
|
|||
assetPrefix?: string,
|
||||
err?: Error|null,
|
||||
nextExport?: boolean,
|
||||
dev?: boolean
|
||||
dev?: boolean,
|
||||
buildManifest: BuildManifest,
|
||||
reactLoadableManifest: ReactLoadableManifest,
|
||||
Component: React.ComponentType,
|
||||
Document: React.ComponentType,
|
||||
App: React.ComponentType,
|
||||
ErrorDebug?: React.ComponentType<{error: Error}>
|
||||
}
|
||||
|
||||
function renderDocument(Document: React.ComponentType, {
|
||||
|
@ -114,20 +115,16 @@ function renderDocument(Document: React.ComponentType, {
|
|||
export async function renderToHTML (req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery, renderOpts: RenderOpts): Promise<string|null> {
|
||||
const {
|
||||
err,
|
||||
buildId,
|
||||
distDir,
|
||||
dev = false,
|
||||
staticMarkup = false
|
||||
staticMarkup = false,
|
||||
App,
|
||||
Document,
|
||||
Component,
|
||||
buildManifest,
|
||||
reactLoadableManifest,
|
||||
ErrorDebug
|
||||
} = renderOpts
|
||||
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')
|
||||
let [buildManifest, reactLoadableManifest, Component, Document, App] = await Promise.all([
|
||||
require(join(distDir, BUILD_MANIFEST)),
|
||||
require(join(distDir, REACT_LOADABLE_MANIFEST)),
|
||||
interoptDefault(requirePage(pathname, distDir)),
|
||||
interoptDefault(require(documentPath)),
|
||||
interoptDefault(require(appPath))
|
||||
])
|
||||
|
||||
|
||||
await Loadable.preloadAll() // Make sure all dynamic imports are loaded
|
||||
|
||||
|
@ -165,14 +162,14 @@ export async function renderToHTML (req: IncomingMessage, res: ServerResponse, p
|
|||
|
||||
const reactLoadableModules: string[] = []
|
||||
const renderPage = (options: ComponentsEnhancer = {}): {html: string, head: any} => {
|
||||
const {App: EnhancedApp, Component: EnhancedComponent} = enhanceComponents(options, App, Component)
|
||||
const renderElementToString = staticMarkup ? renderToStaticMarkup : renderToString
|
||||
|
||||
if(err && dev) {
|
||||
const ErrorDebug = require(join(distDir, SERVER_DIRECTORY, 'error-debug')).default
|
||||
if(err && ErrorDebug) {
|
||||
return render(renderElementToString, <ErrorDebug error={err} />)
|
||||
}
|
||||
|
||||
const {App: EnhancedApp, Component: EnhancedComponent} = enhanceComponents(options, App, Component)
|
||||
|
||||
return render(renderElementToString,
|
||||
<LoadableCapture report={(moduleName) => reactLoadableModules.push(moduleName)}>
|
||||
<EnhancedApp
|
||||
|
|
|
@ -136,6 +136,8 @@ export default async function (dir, options, configuration) {
|
|||
env: process.env
|
||||
})
|
||||
worker.send({
|
||||
distDir,
|
||||
buildId,
|
||||
exportPaths: chunk.paths,
|
||||
exportPathMap: chunk.pathMap,
|
||||
outDir,
|
||||
|
|
|
@ -7,10 +7,13 @@ const mkdirp = require('mkdirp-then')
|
|||
const { renderToHTML } = require('next-server/dist/server/render')
|
||||
const { writeFile } = require('fs')
|
||||
const Sema = require('async-sema')
|
||||
const {loadComponents} = require('next-server/dist/server/load-components')
|
||||
|
||||
process.on(
|
||||
'message',
|
||||
async ({
|
||||
distDir,
|
||||
buildId,
|
||||
exportPaths,
|
||||
exportPathMap,
|
||||
outDir,
|
||||
|
@ -37,7 +40,8 @@ process.on(
|
|||
const htmlFilepath = join(outDir, htmlFilename)
|
||||
|
||||
await mkdirp(baseDir)
|
||||
const html = await renderToHTML(req, res, page, query, renderOpts)
|
||||
const components = await loadComponents(distDir, buildId, page)
|
||||
const html = await renderToHTML(req, res, page, query, {...components, ...renderOpts})
|
||||
await new Promise((resolve, reject) =>
|
||||
writeFile(
|
||||
htmlFilepath,
|
||||
|
|
|
@ -3,11 +3,13 @@ import { join } from 'path'
|
|||
import HotReloader from './hot-reloader'
|
||||
import {route} from 'next-server/dist/server/router'
|
||||
import {PHASE_DEVELOPMENT_SERVER} from 'next-server/constants'
|
||||
import ErrorDebug from './error-debug'
|
||||
|
||||
export default class DevServer extends Server {
|
||||
constructor (options) {
|
||||
super(options)
|
||||
this.renderOpts.dev = true
|
||||
this.renderOpts.ErrorDebug = ErrorDebug
|
||||
this.devReady = new Promise(resolve => {
|
||||
this.setDevReady = resolve
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue