mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 16:12:12 +00:00
move serverMiddleware to renderer
This commit is contained in:
parent
0dabc38785
commit
185dfc1ecf
@ -17,7 +17,7 @@ export default class Nuxt extends Tapable {
|
|||||||
this.renderer = new Renderer(this)
|
this.renderer = new Renderer(this)
|
||||||
|
|
||||||
// Backward compatibility
|
// Backward compatibility
|
||||||
this.render = this.renderer.render.bind(this.renderer)
|
this.render = (...args) => this.renderer.app(...args)
|
||||||
this.renderRoute = this.renderer.renderRoute.bind(this.renderer)
|
this.renderRoute = this.renderer.renderRoute.bind(this.renderer)
|
||||||
this.renderAndGetWindow = this.renderer.renderAndGetWindow.bind(this.renderer)
|
this.renderAndGetWindow = this.renderer.renderAndGetWindow.bind(this.renderer)
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ import _ from 'lodash'
|
|||||||
import { join, resolve } from 'path'
|
import { join, resolve } from 'path'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { createBundleRenderer } from 'vue-server-renderer'
|
import { createBundleRenderer } from 'vue-server-renderer'
|
||||||
import { encodeHtml, getContext, setAnsiColors } from 'utils'
|
import { encodeHtml, getContext, setAnsiColors, isUrl } from 'utils'
|
||||||
import Debug from 'debug'
|
import Debug from 'debug'
|
||||||
|
import connect from 'connect'
|
||||||
|
|
||||||
const debug = Debug('nuxt:render')
|
const debug = Debug('nuxt:render')
|
||||||
debug.color = 4 // Force blue color
|
debug.color = 4 // Force blue color
|
||||||
@ -58,6 +59,9 @@ export default class Renderer extends Tapable {
|
|||||||
this.webpackDevMiddleware = null
|
this.webpackDevMiddleware = null
|
||||||
this.webpackHotMiddleware = null
|
this.webpackHotMiddleware = null
|
||||||
|
|
||||||
|
// Create new connect instance
|
||||||
|
this.app = connect()
|
||||||
|
|
||||||
// Renderer runtime resources
|
// Renderer runtime resources
|
||||||
this.resources = {
|
this.resources = {
|
||||||
clientManifest: null,
|
clientManifest: null,
|
||||||
@ -86,25 +90,16 @@ export default class Renderer extends Tapable {
|
|||||||
return this._ready
|
return this._ready
|
||||||
}
|
}
|
||||||
|
|
||||||
// For serving static/ files to /
|
// Setup all middleWare
|
||||||
this.serveStatic = pify(serveStatic(resolve(this.options.srcDir, 'static'), this.options.render.static))
|
this.setupMiddleware()
|
||||||
|
|
||||||
// For serving .nuxt/dist/ files
|
|
||||||
this.serveStaticNuxt = pify(serveStatic(resolve(this.options.buildDir, 'dist'), {
|
|
||||||
maxAge: (this.options.dev ? 0 : '1y') // 1 year in production
|
|
||||||
}))
|
|
||||||
|
|
||||||
// GZIP middleware for production
|
|
||||||
if (!this.options.dev && this.options.render.gzip) {
|
|
||||||
this.gzipMiddleware = pify(compression(this.options.render.gzip))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Load error template
|
||||||
const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html')
|
const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html')
|
||||||
if (fs.existsSync(errorTemplatePath)) {
|
if (fs.existsSync(errorTemplatePath)) {
|
||||||
this.resources.errorTemplate = parseTemplate(fs.readFileSync(errorTemplatePath, 'utf8'))
|
this.resources.errorTemplate = parseTemplate(fs.readFileSync(errorTemplatePath, 'utf8'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load resources from fs
|
// Load SSR resources from fs
|
||||||
if (!this.options.dev) {
|
if (!this.options.dev) {
|
||||||
await this.loadResources()
|
await this.loadResources()
|
||||||
}
|
}
|
||||||
@ -166,62 +161,105 @@ export default class Renderer extends Tapable {
|
|||||||
this.bundleRenderer.renderToString = pify(this.bundleRenderer.renderToString)
|
this.bundleRenderer.renderToString = pify(this.bundleRenderer.renderToString)
|
||||||
}
|
}
|
||||||
|
|
||||||
async render (req, res) {
|
useMiddleware (m) {
|
||||||
// Get context
|
// Resolve
|
||||||
const context = getContext(req, res)
|
if (typeof m === 'string') {
|
||||||
res.statusCode = 200
|
let src = m
|
||||||
|
// Using ~ or ./ shorthand to resolve from project srcDir
|
||||||
try {
|
if (src.indexOf('~') === 0 || src.indexOf('./') === 0) {
|
||||||
// Gzip middleware for production
|
src = join(this.nuxt.options.srcDir, src.substr(1))
|
||||||
if (this.gzipMiddleware) {
|
}
|
||||||
await this.gzipMiddleware(req, res)
|
m = require(src)
|
||||||
|
}
|
||||||
|
// Use middleware
|
||||||
|
if (m instanceof Function) {
|
||||||
|
this.app.use(m)
|
||||||
|
} else if (m && m.path && m.handler) {
|
||||||
|
this.app.use(m.path, m.handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupMiddleware () {
|
||||||
|
// Gzip middleware for production
|
||||||
|
if (!this.options.dev && this.options.render.gzip) {
|
||||||
|
this.useMiddleware(compression(this.options.render.gzip))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add User provided middleware
|
||||||
|
this.options.serverMiddleware.forEach(m => {
|
||||||
|
this.useMiddleware(m)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Common URL checks
|
||||||
|
this.useMiddleware((req, res, next) => {
|
||||||
// If base in req.url, remove it for the middleware and vue-router
|
// If base in req.url, remove it for the middleware and vue-router
|
||||||
if (this.options.router.base !== '/' && req.url.indexOf(this.options.router.base) === 0) {
|
if (this.options.router.base !== '/' && req.url.indexOf(this.options.router.base) === 0) {
|
||||||
// Compatibility with base url for dev server
|
|
||||||
req.url = req.url.replace(this.options.router.base, '/')
|
req.url = req.url.replace(this.options.router.base, '/')
|
||||||
}
|
}
|
||||||
|
// Prevent access to SSR resources
|
||||||
// Call webpack middleware only in development
|
if (ssrResourceRegex.test(req.url)) {
|
||||||
if (this.webpackDevMiddleware) {
|
|
||||||
await this.webpackDevMiddleware(req, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.webpackHotMiddleware) {
|
|
||||||
await this.webpackHotMiddleware(req, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serve static/ files
|
|
||||||
await this.serveStatic(req, res)
|
|
||||||
|
|
||||||
// Serve .nuxt/dist/ files (only for production)
|
|
||||||
if (!this.options.dev && !ssrResourceRegex.test(req.url)) {
|
|
||||||
const url = req.url
|
|
||||||
if (req.url.indexOf(this.options.build.publicPath) === 0) {
|
|
||||||
req.url = req.url.replace(this.options.build.publicPath, '/')
|
|
||||||
}
|
|
||||||
await this.serveStaticNuxt(req, res)
|
|
||||||
/* istanbul ignore next */
|
|
||||||
req.url = url
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.options.dev && req.url.indexOf(this.options.build.publicPath) === 0 && req.url.includes('.hot-update.json')) {
|
|
||||||
res.statusCode = 404
|
res.statusCode = 404
|
||||||
return res.end()
|
return res.end()
|
||||||
}
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove publicPath from requests when it is pointing to CDN
|
||||||
|
if (isUrl(this.options.build.publicPath)) {
|
||||||
|
this.useMiddleware((req, res, next) => {
|
||||||
|
if (req.url.indexOf(this.options.build.publicPath) === 0) {
|
||||||
|
req.url = req.url.replace(this.options.build.publicPath, '/')
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add webpack middleware only for development
|
||||||
|
if (this.options.dev) {
|
||||||
|
this.useMiddleware(async (req, res, next) => {
|
||||||
|
if (req.url.includes('.hot-update.json')) {
|
||||||
|
res.statusCode = 404
|
||||||
|
return res.end()
|
||||||
|
}
|
||||||
|
if (this.webpackDevMiddleware) {
|
||||||
|
await this.webpackDevMiddleware(req, res)
|
||||||
|
}
|
||||||
|
if (this.webpackHotMiddleware) {
|
||||||
|
await this.webpackHotMiddleware(req, res)
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// For serving static/ files to /
|
||||||
|
this.useMiddleware(serveStatic(resolve(this.options.srcDir, 'static'), this.options.render.static))
|
||||||
|
|
||||||
|
// Serve .nuxt/dist/ files only for production
|
||||||
|
// For dev they will be served with devMiddleware
|
||||||
|
if (!this.options.dev) {
|
||||||
|
this.useMiddleware(serveStatic(resolve(this.options.buildDir, 'dist'), {
|
||||||
|
maxAge: (this.options.dev ? 0 : '1y') // 1 year in production
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally use nuxtMiddleware
|
||||||
|
this.useMiddleware(this.nuxtMiddleware.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
async nuxtMiddleware (req, res) {
|
||||||
|
// Get context
|
||||||
|
const context = getContext(req, res)
|
||||||
|
res.statusCode = 200
|
||||||
|
try {
|
||||||
const { html, error, redirected, resourceHints } = await this.renderRoute(req.url, context)
|
const { html, error, redirected, resourceHints } = await this.renderRoute(req.url, context)
|
||||||
|
|
||||||
if (redirected) {
|
if (redirected) {
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
res.statusCode = context.nuxt.error.statusCode || 500
|
res.statusCode = context.nuxt.error.statusCode || 500
|
||||||
}
|
}
|
||||||
|
|
||||||
// ETag header
|
// Add ETag header
|
||||||
if (!error && this.options.render.etag) {
|
if (!error && this.options.render.etag) {
|
||||||
const etag = generateETag(html, this.options.render.etag)
|
const etag = generateETag(html, this.options.render.etag)
|
||||||
if (fresh(req.headers, { etag })) {
|
if (fresh(req.headers, { etag })) {
|
||||||
@ -263,7 +301,6 @@ export default class Renderer extends Tapable {
|
|||||||
}
|
}
|
||||||
// Render error template
|
// Render error template
|
||||||
const html = this.resources.errorTemplate({
|
const html = this.resources.errorTemplate({
|
||||||
/* istanbul ignore if */
|
|
||||||
error: err,
|
error: err,
|
||||||
stack: ansiHTML(encodeHtml(err.stack))
|
stack: ansiHTML(encodeHtml(err.stack))
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import http from 'http'
|
import http from 'http'
|
||||||
import connect from 'connect'
|
|
||||||
import path from 'path'
|
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
@ -20,6 +18,9 @@ class Server {
|
|||||||
return this._ready
|
return this._ready
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop server on nuxt.close()
|
||||||
|
this.nuxt.plugin('close', () => this.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
async ready () {
|
async ready () {
|
||||||
@ -28,59 +29,34 @@ class Server {
|
|||||||
return this._ready
|
return this._ready
|
||||||
}
|
}
|
||||||
|
|
||||||
this.app = connect()
|
this.render = this.nuxt.renderer.app
|
||||||
this.server = http.createServer(this.app)
|
this.server = http.createServer(this.render)
|
||||||
|
|
||||||
// Add Middleware
|
|
||||||
this.options.serverMiddleware.forEach(m => {
|
|
||||||
this.useMiddleware(m)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add default render middleware
|
|
||||||
this.useMiddleware(this.render.bind(this))
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
useMiddleware (m) {
|
|
||||||
// Require if needed
|
|
||||||
if (typeof m === 'string') {
|
|
||||||
let src = m
|
|
||||||
// Using ~ or ./ shorthand to resolve from project srcDir
|
|
||||||
if (src.indexOf('~') === 0 || src.indexOf('./') === 0) {
|
|
||||||
src = path.join(this.nuxt.options.srcDir, src.substr(1))
|
|
||||||
}
|
|
||||||
m = require(src)
|
|
||||||
}
|
|
||||||
if (m instanceof Function) {
|
|
||||||
this.app.use(m)
|
|
||||||
} else if (m && m.path && m.handler) {
|
|
||||||
this.app.use(m.path, m.handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render (req, res, next) {
|
|
||||||
this.nuxt.render(req, res)
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
listen (port, host) {
|
listen (port, host) {
|
||||||
host = host || 'localhost'
|
host = host || 'localhost'
|
||||||
port = port || 3000
|
port = port || 3000
|
||||||
this.nuxt.ready()
|
return this.ready()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.server.listen(port, host, () => {
|
this.server.listen(port, host, () => {
|
||||||
let _host = host === '0.0.0.0' ? 'localhost' : host
|
let _host = host === '0.0.0.0' ? 'localhost' : host
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('\n' + chalk.bold(chalk.bgBlue.black(' OPEN ') + chalk.blue(` http://${_host}:${port}\n`)))
|
console.log('\n' + chalk.bold(chalk.bgBlue.black(' OPEN ') + chalk.blue(` http://${_host}:${port}\n`)))
|
||||||
})
|
})
|
||||||
})
|
}).catch(this.nuxt.errorHandler)
|
||||||
.catch(this.nuxt.errorHandler)
|
|
||||||
return this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close (cb) {
|
close () {
|
||||||
return this.server.close(cb)
|
return new Promise((resolve, reject) => {
|
||||||
|
this.server.close(err => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user