1
0
Fork 0
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:
Tim Neutkens 2018-12-18 17:12:49 +01:00 committed by GitHub
parent cd0a1767f4
commit 32451e979e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 70 additions and 32 deletions

View file

@ -5,7 +5,7 @@ export type ManifestItem = {
publicPath: string
}
type Manifest = {[moduleId: string]: ManifestItem[]}
export type Manifest = {[moduleId: string]: ManifestItem[]}
type DynamicImportBundles = Set<ManifestItem>

View file

@ -1,6 +1,7 @@
import {normalizePagePath} from './require'
type BuildManifest = {
export type BuildManifest = {
devFiles: string[],
pages: {
[page: string]: string[]
}

View 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}
}

View file

@ -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> {

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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
})