Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve production debugging dx #7463

Merged
merged 14 commits into from Jun 9, 2020
12 changes: 6 additions & 6 deletions packages/config/src/config/build.js
Expand Up @@ -14,12 +14,12 @@ export default () => ({
serverURLPolyfill: 'url',
filenames: {
// { isDev, isClient, isServer }
app: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[contenthash].js',
chunk: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[contenthash].js',
css: ({ isDev }) => isDev ? '[name].css' : '[contenthash].css',
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[contenthash:7].[ext]',
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[contenthash:7].[ext]'
app: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[name].[contenthash:7].js',
chunk: ({ isDev, isModern }) => isDev ? `${isModern ? 'modern-' : ''}[name].js` : '[name].[contenthash:7].js',
css: ({ isDev }) => isDev ? '[name].css' : '[name].[contenthash:7].css',
manniL marked this conversation as resolved.
Show resolved Hide resolved
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[name].[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[name].[contenthash:7].[ext]',
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[name].[contenthash:7].[ext]'
},
loaders: {
file: {},
Expand Down
12 changes: 6 additions & 6 deletions packages/config/test/config/build.test.js
Expand Up @@ -15,12 +15,12 @@ describe('config: build', () => {
test('should return prod filenames', () => {
const { filenames } = buildConfig()
const env = { isDev: false }
expect(filenames.app(env)).toEqual('[contenthash].js')
expect(filenames.chunk(env)).toEqual('[contenthash].js')
expect(filenames.css(env)).toEqual('[contenthash].css')
expect(filenames.img(env)).toEqual('img/[contenthash:7].[ext]')
expect(filenames.font(env)).toEqual('fonts/[contenthash:7].[ext]')
expect(filenames.video(env)).toEqual('videos/[contenthash:7].[ext]')
expect(filenames.app(env)).toEqual('[name].[contenthash:7].js')
expect(filenames.chunk(env)).toEqual('[name].[contenthash:7].js')
expect(filenames.css(env)).toEqual('[name].[contenthash:7].css')
expect(filenames.img(env)).toEqual('img/[name].[contenthash:7].[ext]')
expect(filenames.font(env)).toEqual('fonts/[name].[contenthash:7].[ext]')
expect(filenames.video(env)).toEqual('videos/[name].[contenthash:7].[ext]')
})

test('should return modern filenames', () => {
Expand Down
78 changes: 46 additions & 32 deletions packages/server/src/middleware/error.js
Expand Up @@ -4,9 +4,11 @@ import consola from 'consola'

import Youch from '@nuxtjs/youch'

export default ({ resources, options }) => async function errorMiddleware (err, req, res, next) {
// ensure statusCode, message and name fields
export default ({ resources, options }) => async function errorMiddleware (rawError, req, res, next) {
// Normalize error
const err = normalizeStack(rawError, options)

// ensure statusCode, message and name fields
const error = {
statusCode: err.statusCode || 500,
message: err.message || 'Nuxt Server Error',
Expand Down Expand Up @@ -46,6 +48,7 @@ export default ({ resources, options }) => async function errorMiddleware (err,
if (err.statusCode !== 404) {
consola.error(err)
}

// Json format is compatible with Youch json responses
const json = {
status: error.statusCode,
Expand All @@ -61,19 +64,9 @@ export default ({ resources, options }) => async function errorMiddleware (err,
return
}

const errorFull = err instanceof Error
? err
: typeof err === 'string'
? new Error(err)
: new Error(err.message || JSON.stringify(err))

errorFull.name = error.name
errorFull.statusCode = error.statusCode
errorFull.stack = err.stack || undefined

// Show stack trace
const youch = new Youch(
errorFull,
err,
req,
readSourceFactory({
srcDir: options.srcDir,
Expand All @@ -93,17 +86,13 @@ export default ({ resources, options }) => async function errorMiddleware (err,
sendResponse(html)
}

const readSourceFactory = ({ srcDir, rootDir, buildDir }) => async function readSource (frame) {
// Remove webpack:/// & query string from the end
const sanitizeName = name => name ? name.replace('webpack:///', '').split('?')[0] : null
frame.fileName = sanitizeName(frame.fileName)
const sanitizeName = name => name ? name.replace('webpack:///', '').split('?')[0] : null

// Return if fileName is unknown
if (!frame.fileName) {
return
}
const normalizeStack = (_err, { srcDir, rootDir, buildDir }) => {
const err = (_err instanceof Error || typeof _err === 'string')
? new Error(_err)
: new Error(_err.message || JSON.stringify(_err))

// Possible paths for file
const searchPath = [
srcDir,
rootDir,
Expand All @@ -112,17 +101,42 @@ const readSourceFactory = ({ srcDir, rootDir, buildDir }) => async function read
process.cwd()
]

// Scan filesystem for real source
for (const pathDir of searchPath) {
const fullPath = path.resolve(pathDir, frame.fileName)
const source = await fs.readFile(fullPath, 'utf-8').catch(() => null)
if (source) {
frame.contents = source
frame.fullPath = fullPath
if (path.isAbsolute(frame.fileName)) {
frame.fileName = path.relative(rootDir, fullPath)
const findInPaths = (fileName) => {
for (const dir of searchPath) {
const fullPath = path.resolve(dir, fileName)
if (fs.existsSync(fullPath)) {
return fullPath
}
return
}
return fileName
}

err.stack = (_err.stack || '')
.split('\n')
.map((l) => {
const m = l.match(/\(([^)]+)\)|([^\s]+\.[^\s]+):/)
pi0 marked this conversation as resolved.
Show resolved Hide resolved
if (!m) {
return l
}
const src = m[1] || m[2] || ''
const s = src.split(':')
if (s[0]) {
s[0] = findInPaths(sanitizeName(s[0]))
}
return l.replace(src, s.join(':'))
})
.join('\n')

return err
}

const readSourceFactory = ({ rootDir }) => async function readSource (frame) {
const source = await fs.readFile(frame.fileName, 'utf-8').catch(() => null)
if (source) {
frame.contents = source
if (path.isAbsolute(frame.fileName)) {
frame.fullPath = frame.fileName
frame.fileName = path.relative(rootDir, frame.fileName)
}
}
}
3 changes: 2 additions & 1 deletion packages/webpack/src/config/server.js
Expand Up @@ -41,7 +41,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
optimization () {
return {
splitChunks: false,
minimizer: this.minimizer()
minimizer: []
}
}

Expand Down Expand Up @@ -96,6 +96,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
}),
output: Object.assign({}, config.output, {
filename: 'server.js',
chunkFilename: '[name].js',
libraryTarget: 'commonjs2'
}),
performance: {
Expand Down