mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-23 22:25: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)
|
||||
|
||||
// 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.renderAndGetWindow = this.renderer.renderAndGetWindow.bind(this.renderer)
|
||||
|
||||
|
@ -10,8 +10,9 @@ import _ from 'lodash'
|
||||
import { join, resolve } from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { createBundleRenderer } from 'vue-server-renderer'
|
||||
import { encodeHtml, getContext, setAnsiColors } from 'utils'
|
||||
import { encodeHtml, getContext, setAnsiColors, isUrl } from 'utils'
|
||||
import Debug from 'debug'
|
||||
import connect from 'connect'
|
||||
|
||||
const debug = Debug('nuxt:render')
|
||||
debug.color = 4 // Force blue color
|
||||
@ -58,6 +59,9 @@ export default class Renderer extends Tapable {
|
||||
this.webpackDevMiddleware = null
|
||||
this.webpackHotMiddleware = null
|
||||
|
||||
// Create new connect instance
|
||||
this.app = connect()
|
||||
|
||||
// Renderer runtime resources
|
||||
this.resources = {
|
||||
clientManifest: null,
|
||||
@ -86,25 +90,16 @@ export default class Renderer extends Tapable {
|
||||
return this._ready
|
||||
}
|
||||
|
||||
// For serving static/ files to /
|
||||
this.serveStatic = pify(serveStatic(resolve(this.options.srcDir, 'static'), this.options.render.static))
|
||||
|
||||
// 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))
|
||||
}
|
||||
// Setup all middleWare
|
||||
this.setupMiddleware()
|
||||
|
||||
// Load error template
|
||||
const errorTemplatePath = resolve(this.options.buildDir, 'views/error.html')
|
||||
if (fs.existsSync(errorTemplatePath)) {
|
||||
this.resources.errorTemplate = parseTemplate(fs.readFileSync(errorTemplatePath, 'utf8'))
|
||||
}
|
||||
|
||||
// Load resources from fs
|
||||
// Load SSR resources from fs
|
||||
if (!this.options.dev) {
|
||||
await this.loadResources()
|
||||
}
|
||||
@ -166,62 +161,105 @@ export default class Renderer extends Tapable {
|
||||
this.bundleRenderer.renderToString = pify(this.bundleRenderer.renderToString)
|
||||
}
|
||||
|
||||
async render (req, res) {
|
||||
// Get context
|
||||
const context = getContext(req, res)
|
||||
res.statusCode = 200
|
||||
|
||||
try {
|
||||
// Gzip middleware for production
|
||||
if (this.gzipMiddleware) {
|
||||
await this.gzipMiddleware(req, res)
|
||||
useMiddleware (m) {
|
||||
// Resolve
|
||||
if (typeof m === 'string') {
|
||||
let src = m
|
||||
// Using ~ or ./ shorthand to resolve from project srcDir
|
||||
if (src.indexOf('~') === 0 || src.indexOf('./') === 0) {
|
||||
src = join(this.nuxt.options.srcDir, src.substr(1))
|
||||
}
|
||||
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 (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, '/')
|
||||
}
|
||||
|
||||
// Call webpack middleware only in development
|
||||
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')) {
|
||||
// Prevent access to SSR resources
|
||||
if (ssrResourceRegex.test(req.url)) {
|
||||
res.statusCode = 404
|
||||
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)
|
||||
|
||||
if (redirected) {
|
||||
return html
|
||||
}
|
||||
|
||||
if (error) {
|
||||
res.statusCode = context.nuxt.error.statusCode || 500
|
||||
}
|
||||
|
||||
// ETag header
|
||||
// Add ETag header
|
||||
if (!error && this.options.render.etag) {
|
||||
const etag = generateETag(html, this.options.render.etag)
|
||||
if (fresh(req.headers, { etag })) {
|
||||
@ -263,7 +301,6 @@ export default class Renderer extends Tapable {
|
||||
}
|
||||
// Render error template
|
||||
const html = this.resources.errorTemplate({
|
||||
/* istanbul ignore if */
|
||||
error: err,
|
||||
stack: ansiHTML(encodeHtml(err.stack))
|
||||
})
|
||||
|
@ -1,6 +1,4 @@
|
||||
import http from 'http'
|
||||
import connect from 'connect'
|
||||
import path from 'path'
|
||||
import chalk from 'chalk'
|
||||
|
||||
class Server {
|
||||
@ -20,6 +18,9 @@ class Server {
|
||||
return this._ready
|
||||
})
|
||||
}
|
||||
|
||||
// Stop server on nuxt.close()
|
||||
this.nuxt.plugin('close', () => this.close())
|
||||
}
|
||||
|
||||
async ready () {
|
||||
@ -28,59 +29,34 @@ class Server {
|
||||
return this._ready
|
||||
}
|
||||
|
||||
this.app = connect()
|
||||
this.server = http.createServer(this.app)
|
||||
this.render = this.nuxt.renderer.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
|
||||
}
|
||||
|
||||
listen (port, host) {
|
||||
host = host || 'localhost'
|
||||
port = port || 3000
|
||||
this.nuxt.ready()
|
||||
return this.ready()
|
||||
.then(() => {
|
||||
this.server.listen(port, host, () => {
|
||||
let _host = host === '0.0.0.0' ? 'localhost' : host
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\n' + chalk.bold(chalk.bgBlue.black(' OPEN ') + chalk.blue(` http://${_host}:${port}\n`)))
|
||||
})
|
||||
})
|
||||
.catch(this.nuxt.errorHandler)
|
||||
return this
|
||||
}).catch(this.nuxt.errorHandler)
|
||||
}
|
||||
|
||||
close (cb) {
|
||||
return this.server.close(cb)
|
||||
close () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.close(err => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user