Nuxt/lib/core/middleware/error.js

112 lines
3.0 KiB
JavaScript
Raw Normal View History

2018-03-16 19:11:24 +00:00
import path from 'path'
import consola from 'consola'
2018-03-16 19:52:17 +00:00
import Youch from '@nuxtjs/youch'
2018-03-16 19:11:24 +00:00
import fs from 'fs-extra'
2018-03-16 16:12:06 +00:00
export default function errorMiddleware(err, req, res, next) {
// ensure statusCode, message and name fields
err.statusCode = err.statusCode || 500
err.message = err.message || 'Nuxt Server Error'
2018-01-13 05:22:11 +00:00
err.name = !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name
// We hide actual errors from end users, so show them on server logs
if (err.statusCode !== 404) {
2018-04-01 20:20:46 +00:00
consola.error(err)
}
const sendResponse = (content, type = 'text/html') => {
// Set Headers
res.statusCode = err.statusCode
res.statusMessage = err.name
res.setHeader('Content-Type', type + '; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(content))
// Send Response
res.end(content, 'utf-8')
}
// Check if request accepts JSON
2018-01-13 05:22:11 +00:00
const hasReqHeader = (header, includes) =>
req.headers[header] && req.headers[header].toLowerCase().includes(includes)
const isJson =
hasReqHeader('accept', 'application/json') ||
hasReqHeader('user-agent', 'curl/')
// Use basic errors when debug mode is disabled
if (!this.options.debug) {
// Json format is compatible with Youch json responses
const json = {
status: err.statusCode,
message: err.message,
name: err.name
}
if (isJson) {
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
return
}
const html = this.resources.errorTemplate(json)
sendResponse(html)
return
}
// Show stack trace
2018-01-13 05:22:11 +00:00
const youch = new Youch(
err,
req,
readSource.bind(this),
this.options.router.base,
true
)
if (isJson) {
youch.toJSON().then((json) => {
2018-01-13 05:22:11 +00:00
sendResponse(JSON.stringify(json, undefined, 2), 'text/json')
})
} else {
youch.toHTML().then(html => sendResponse(html))
}
}
async function readSource(frame) {
// Remove webpack:/// & query string from the end
2018-01-13 05:22:11 +00:00
const sanitizeName = name =>
name ? name.replace('webpack:///', '').split('?')[0] : null
frame.fileName = sanitizeName(frame.fileName)
2018-01-05 09:19:32 +00:00
// Return if fileName is unknown
/* istanbul ignore if */
2018-01-05 09:19:32 +00:00
if (!frame.fileName) {
return
}
// Possible paths for file
const searchPath = [
2018-01-05 09:19:32 +00:00
this.options.srcDir,
this.options.rootDir,
path.join(this.options.buildDir, 'dist', 'server'),
2018-01-07 08:21:36 +00:00
this.options.buildDir,
process.cwd()
]
2018-01-05 09:19:32 +00:00
// Scan filesystem for real source
2018-08-08 10:54:05 +00:00
for (const pathDir of searchPath) {
const fullPath = path.resolve(pathDir, frame.fileName)
const source = await fs.readFile(fullPath, 'utf-8').catch(() => null)
if (source) {
2018-01-05 09:19:32 +00:00
frame.contents = source
frame.fullPath = fullPath
2018-03-16 19:11:24 +00:00
if (path.isAbsolute(frame.fileName)) {
frame.fileName = path.relative(this.options.rootDir, fullPath)
2018-01-05 09:19:32 +00:00
}
return
}
}
2018-01-05 09:19:32 +00:00
// Fallback: use server bundle
// TODO: restore to if after https://github.com/istanbuljs/nyc/issues/595 fixed
/* istanbul ignore next */
2018-01-05 09:19:32 +00:00
if (!frame.contents) {
frame.contents = this.resources.serverBundle.files[frame.fileName]
}
}