Repository: nuxt/nuxt.js
Pull request #7463 (2020-06-04 14:11:40)

feat: improve production debugging dx
<!--- Provide a general summary of your changes in the title above -->

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (a non-breaking change which fixes an issue)
- [x] New feature (a non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)


## Description

### Resolve SSR stack trace paths

(outputs modified for readability. consola has a fancy logger that does some similar magic but only for display)

Old:

```
ReferenceError: crashme is not defined                                                                                                                                      16:07:17
    at asyncData (examples/hello-world/pages/index.vue:13:0)
    at promisify (examples/hello-world/.nuxt/utils.js:259:0)
    at server.js:1:<col>
    at Array.map (<anonymous>)
    at module.exports.__webpack_exports__.default (examples/hello-world/.nuxt/server.js:227:0)
```

New:

```
ReferenceError: crashme is not defined                                                                                                                                      
    at asyncData ([path]/examples/hello-world/pages/index.vue:13:0)
    at promisify ([path]/examples/hello-world/.nuxt/utils.js:259:0)
    at [path]/examples/hello-world/.nuxt/dist/server/server.js:2582:23
    at Array.map (<anonymous>)
    at module.exports.__webpack_exports__.default ([path]/examples/hello-world/.nuxt/server.js:227:0)
```

### Make chunk names easy to debug

Old:

```
├── client
│   ├── 421fcb3837a373bcc316.js
│   ├── 43f6a8fec12d37fd9731.js
│   ├── 923c20ce304abf61b7d0.js
│   ├── f4d7116a13cbd5bad38e.js
│   ├── fb4666c8451b74cab31e.js
│   └── LICENSES
└── server
    ├── 7250d0198e376567f352.js
    ├── ae78742f8bb4962f2610.js
    ├── client.manifest.json
    ├── index.spa.html
    ├── index.ssr.html
    ├── server.js
    └── server.manifest.json
```

New:

```
├── client
│   ├── app.43f6a8f.js
│   ├── commons.app.f4d7116.js
│   ├── LICENSES
│   ├── pages
│   │   ├── about.923c20c.js
│   │   └── index.421fcb3.js
│   └── runtime.e630eac.js
└── server
    ├── client.manifest.json
    ├── index.spa.html
    ├── index.ssr.html
    ├── pages
    │   ├── about.js
    │   ├── about.js.map
    │   ├── index.js
    │   └── index.js.map
    ├── server.js
    ├── server.js.map
    └── server.manifest.json
```

Also, server.js is no longer minimized by terser. This helps making build little faster and also having correct sourcemap lines.

## Checklist:
<!--- Put an `x` in all the boxes that apply. -->
<!--- If your change requires a documentation PR, please link it appropriately -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly. (PR: #)
- [ ] I have added tests to cover my changes (if not applicable, please state why)
- [ ] All new and existing tests are passing.



lihbr: This is so cool, hope it'll land on time for 2.13.x 👏
codecov-commenter: # [Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=h1) Report
> Merging [#7463](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=desc) into [dev](https://codecov.io/gh/nuxt/nuxt.js/commit/893cfe79d3a8bc06f8a610694538e10229051bf7&el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `92.50%`.

[![Impacted file tree graph](https://codecov.io/gh/nuxt/nuxt.js/pull/7463/graphs/tree.svg?width=650&height=150&src=pr&token=nGD1PtMB3M)](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##              dev    #7463      +/-   ##
==========================================
- Coverage   70.32%   70.30%   -0.03%     
==========================================
  Files          88       88              
  Lines        3704     3708       +4     
  Branches     1010     1006       -4     
==========================================
+ Hits         2605     2607       +2     
- Misses        892      894       +2     
  Partials      207      207              
```

| Flag | Coverage Δ | |
|---|---|---|
| #unittests | `70.30% <92.50%> (-0.03%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/webpack/src/config/server.js](https://codecov.io/gh/nuxt/nuxt.js/pull/7463/diff?src=pr&el=tree#diff-cGFja2FnZXMvd2VicGFjay9zcmMvY29uZmlnL3NlcnZlci5qcw==) | `0.00% <ø> (ø)` | |
| [packages/server/src/middleware/error.js](https://codecov.io/gh/nuxt/nuxt.js/pull/7463/diff?src=pr&el=tree#diff-cGFja2FnZXMvc2VydmVyL3NyYy9taWRkbGV3YXJlL2Vycm9yLmpz) | `95.23% <91.17%> (-3.07%)` | :arrow_down: |
| [packages/config/src/config/build.js](https://codecov.io/gh/nuxt/nuxt.js/pull/7463/diff?src=pr&el=tree#diff-cGFja2FnZXMvY29uZmlnL3NyYy9jb25maWcvYnVpbGQuanM=) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=footer). Last update [893cfe7...90b09ce](https://codecov.io/gh/nuxt/nuxt.js/pull/7463?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

pimlie: @pi0 Is there a measurable difference in parsing/loading server.js if not minimized? When you run a node server then that shouldn't matter much, but if you are using a lambda then that server.js could need to be parsed on almost every request.
pi0: @pimlie Agree however we are not currently officially supporting serverless target. Builders/Packers may utilize a minimizer to reduce size. I added `build._minifyServer` to make it easier until we improve support wdyt?
pimlie: As long as its still configurable I am happy, thanks ;)

Patch:
diff --git a/packages/config/src/config/build.js b/packages/config/src/config/build.js
index ae72a82e8..b45d01e19 100644
--- a/packages/config/src/config/build.js
+++ b/packages/config/src/config/build.js
@@ -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',
+    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: {},
diff --git a/packages/server/src/middleware/error.js b/packages/server/src/middleware/error.js
index 05e61ffaa..9603281b8 100644
--- a/packages/server/src/middleware/error.js
+++ b/packages/server/src/middleware/error.js
@@ -4,20 +4,14 @@ 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
-
-  const error = {
-    statusCode: err.statusCode || 500,
-    message: err.message || 'Nuxt Server Error',
-    name: !err.name || err.name === 'Error' ? 'NuxtServerError' : err.name,
-    headers: err.headers
-  }
+export default ({ resources, options }) => async function errorMiddleware (_error, req, res, next) {
+  // Normalize error
+  const error = normalizeError(_error, options)
 
   const sendResponse = (content, type = 'text/html') => {
     // Set Headers
     res.statusCode = error.statusCode
-    res.statusMessage = error.name
+    res.statusMessage = 'RuntimeError'
     res.setHeader('Content-Type', type + '; charset=utf-8')
     res.setHeader('Content-Length', Buffer.byteLength(content))
     res.setHeader('Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
@@ -43,9 +37,10 @@ export default ({ resources, options }) => async function errorMiddleware (err,
   // Use basic errors when debug mode is disabled
   if (!options.debug) {
     // We hide actual errors from end users, so show them on server logs
-    if (err.statusCode !== 404) {
-      consola.error(err)
+    if (error.statusCode !== 404) {
+      consola.error(error)
     }
+
     // Json format is compatible with Youch json responses
     const json = {
       status: error.statusCode,
@@ -61,25 +56,11 @@ 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,
+    error,
     req,
-    readSourceFactory({
-      srcDir: options.srcDir,
-      rootDir: options.rootDir,
-      buildDir: options.buildDir
-    }),
+    readSource,
     options.router.base,
     true
   )
@@ -93,17 +74,21 @@ 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 normalizeError = (_error, { srcDir, rootDir, buildDir }) => {
+  if (typeof _error === 'string') {
+    _error = { message: _error }
+  } else if (!_error) {
+    _error = { message: '<empty>' }
   }
 
-  // Possible paths for file
+  const error = new Error()
+  error.message = _error.message
+  error.name = _error.name
+  error.statusCode = _error.statusCode || 500
+  error.headers = _error.headers
+
   const searchPath = [
     srcDir,
     rootDir,
@@ -112,17 +97,34 @@ 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
+  }
+
+  error.stack = (_error.stack || '')
+    .split('\n')
+    .map((line) => {
+      const match = line.match(/\(([^)]+)\)|([^\s]+\.[^\s]+):/)
+      if (!match) {
+        return line
+      }
+      const src = match[1] || match[2] || ''
+      return line.replace(src, findInPaths(sanitizeName(src)))
+    })
+    .join('\n')
+
+  return error
+}
+
+async function readSource (frame) {
+  if (fs.existsSync(frame.fileName)) {
+    frame.fullPath = frame.fileName // Youch BW compat
+    frame.contents = await fs.readFile(frame.fileName, 'utf-8')
   }
 }
diff --git a/packages/webpack/src/config/server.js b/packages/webpack/src/config/server.js
index d921c4330..f6185f4ad 100644
--- a/packages/webpack/src/config/server.js
+++ b/packages/webpack/src/config/server.js
@@ -56,9 +56,11 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
   }
 
   optimization () {
+    const { _minifyServer } = this.buildContext.buildOptions
+
     return {
       splitChunks: false,
-      minimizer: this.minimizer()
+      minimizer: _minifyServer ? this.minimizer() : []
     }
   }
 
@@ -113,6 +115,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
       }),
       output: Object.assign({}, config.output, {
         filename: 'server.js',
+        chunkFilename: '[name].js',
         libraryTarget: 'commonjs2'
       }),
       performance: {


Test patch:
diff --git a/packages/cli/test/unit/__snapshots__/webpack.test.js.snap b/packages/cli/test/unit/__snapshots__/webpack.test.js.snap
index b5000549c..31e9d84ba 100644
--- a/packages/cli/test/unit/__snapshots__/webpack.test.js.snap
+++ b/packages/cli/test/unit/__snapshots__/webpack.test.js.snap
@@ -567,7 +567,7 @@ exports[`webpack nuxt webpack module.rules 1`] = `
         \\"loader\\": \\"url-loader\\",
         \\"options\\": Object {
           \\"limit\\": 1000,
-          \\"name\\": \\"img/[contenthash:7].[ext]\\",
+          \\"name\\": \\"img/[name].[contenthash:7].[ext]\\",
         },
       },
     ],
@@ -579,7 +579,7 @@ exports[`webpack nuxt webpack module.rules 1`] = `
         \\"loader\\": \\"url-loader\\",
         \\"options\\": Object {
           \\"limit\\": 1000,
-          \\"name\\": \\"fonts/[contenthash:7].[ext]\\",
+          \\"name\\": \\"fonts/[name].[contenthash:7].[ext]\\",
         },
       },
     ],
@@ -590,7 +590,7 @@ exports[`webpack nuxt webpack module.rules 1`] = `
       Object {
         \\"loader\\": \\"file-loader\\",
         \\"options\\": Object {
-          \\"name\\": \\"videos/[contenthash:7].[ext]\\",
+          \\"name\\": \\"videos/[name].[contenthash:7].[ext]\\",
         },
       },
     ],
diff --git a/packages/config/test/config/build.test.js b/packages/config/test/config/build.test.js
index f55f79e6e..f9336df0e 100644
--- a/packages/config/test/config/build.test.js
+++ b/packages/config/test/config/build.test.js
@@ -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', () => {
diff --git a/packages/server/test/middleware/error.test.js b/packages/server/test/middleware/error.test.js
index fd8e5a09d..92421f37a 100644
--- a/packages/server/test/middleware/error.test.js
+++ b/packages/server/test/middleware/error.test.js
@@ -29,6 +29,13 @@ const createServerContext = () => ({
   next: jest.fn()
 })
 
+const errorFileName = 'test-error.js'
+const createError = () => {
+  const err = new Error('Error!')
+  err.stack = `Error!\n  at foo (webpack:///${errorFileName}?foo:1)`
+  return err
+}
+
 describe('server: errorMiddleware', () => {
   beforeAll(() => {
     path.join.mockImplementation((...args) => `join(${args.join(', ')})`)
@@ -48,26 +55,21 @@ describe('server: errorMiddleware', () => {
   test('should send html error response', async () => {
     const params = createParams()
     const errorMiddleware = createErrorMiddleware(params)
-    const error = new Error()
+    const error = {}
     error.headers = { 'Custom-Header': 'test' }
     const ctx = createServerContext()
 
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
-    expect(consola.error).toBeCalledWith(error)
     expect(ctx.res.statusCode).toEqual(500)
-    expect(ctx.res.statusMessage).toEqual('NuxtServerError')
+    expect(ctx.res.statusMessage).toEqual('RuntimeError')
     expect(ctx.res.setHeader).toBeCalledTimes(4)
     expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/html; charset=utf-8')
     expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength('error template'))
     expect(ctx.res.setHeader).nthCalledWith(3, 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate')
     expect(ctx.res.setHeader).nthCalledWith(4, 'Custom-Header', 'test')
     expect(params.resources.errorTemplate).toBeCalledTimes(1)
-    expect(params.resources.errorTemplate).toBeCalledWith({
-      status: 500,
-      message: 'Nuxt Server Error',
-      name: 'NuxtServerError'
-    })
+    expect(params.resources.errorTemplate).toBeCalledWith({ status: 500 })
     expect(ctx.res.end).toBeCalledTimes(1)
     expect(ctx.res.end).toBeCalledWith('error template', 'utf-8')
   })
@@ -77,7 +79,6 @@ describe('server: errorMiddleware', () => {
     const errorMiddleware = createErrorMiddleware(params)
     const error = {
       statusCode: 404,
-      name: 'NuxtTestError',
       message: 'test error'
     }
     const ctx = createServerContext()
@@ -92,7 +93,6 @@ describe('server: errorMiddleware', () => {
     }, undefined, 2)
     expect(consola.error).not.toBeCalled()
     expect(ctx.res.statusCode).toEqual(404)
-    expect(ctx.res.statusMessage).toEqual(error.name)
     expect(ctx.res.setHeader).toBeCalledTimes(3)
     expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/json; charset=utf-8')
     expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errJson))
@@ -108,22 +108,13 @@ describe('server: errorMiddleware', () => {
     const errorMiddleware = createErrorMiddleware(params)
     const error = new Error('test error')
     error.statusCode = 503
-    error.name = 'NuxtTestError'
     const ctx = createServerContext()
 
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
     const errHtml = 'youch html'
-    expect(Youch).toBeCalledTimes(1)
-    expect(Youch).toBeCalledWith(
-      error,
-      ctx.req,
-      expect.any(Function),
-      params.options.router.base,
-      true
-    )
+
     expect(ctx.res.statusCode).toEqual(503)
-    expect(ctx.res.statusMessage).toEqual(error.name)
     expect(ctx.res.setHeader).toBeCalledTimes(3)
     expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/html; charset=utf-8')
     expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errHtml))
@@ -139,7 +130,6 @@ describe('server: errorMiddleware', () => {
     const errorMiddleware = createErrorMiddleware(params)
     const error = {
       statusCode: 404,
-      name: 'NuxtTestError',
       message: 'test error'
     }
     const ctx = createServerContext()
@@ -148,20 +138,8 @@ describe('server: errorMiddleware', () => {
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
     const errJson = JSON.stringify('youch json', undefined, 2)
-    const errorFull = new Error(error.message)
-    errorFull.name = error.name
-    errorFull.statusCode = error.statusCode
-    errorFull.stack = undefined
-    expect(Youch).toBeCalledTimes(1)
-    expect(Youch).toBeCalledWith(
-      errorFull,
-      ctx.req,
-      expect.any(Function),
-      params.options.router.base,
-      true
-    )
+
     expect(ctx.res.statusCode).toEqual(404)
-    expect(ctx.res.statusMessage).toEqual(error.name)
     expect(ctx.res.setHeader).toBeCalledTimes(3)
     expect(ctx.res.setHeader).nthCalledWith(1, 'Content-Type', 'text/json; charset=utf-8')
     expect(ctx.res.setHeader).nthCalledWith(2, 'Content-Length', Buffer.byteLength(errJson))
@@ -175,24 +153,16 @@ describe('server: errorMiddleware', () => {
     const params = createParams()
     params.options.debug = true
     const errorMiddleware = createErrorMiddleware(params)
-    const error = {}
+    const error = createError()
     const ctx = createServerContext()
 
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
-    const frame = { fileName: 'webpack:///test-error.js?desc=test' }
-    const readSource = Youch.mock.calls[0][2]
-    await readSource(frame)
-
-    const fileName = 'test-error.js'
-    expect(frame).toEqual({ fileName })
-    expect(path.resolve).toBeCalledTimes(5)
-    expect(fs.readFile).toBeCalledTimes(5)
-    expect(fs.readFile).nthCalledWith(1, `resolve(${params.options.srcDir}, ${fileName})`, 'utf-8')
-    expect(fs.readFile).nthCalledWith(2, `resolve(${params.options.rootDir}, ${fileName})`, 'utf-8')
-    expect(fs.readFile).nthCalledWith(3, `resolve(join(${params.options.buildDir}, dist, server), ${fileName})`, 'utf-8')
-    expect(fs.readFile).nthCalledWith(4, `resolve(${params.options.buildDir}, ${fileName})`, 'utf-8')
-    expect(fs.readFile).nthCalledWith(5, `resolve(${process.cwd()}, ${fileName})`, 'utf-8')
+    expect(fs.existsSync).nthCalledWith(1, `resolve(${params.options.srcDir}, ${errorFileName})`)
+    expect(fs.existsSync).nthCalledWith(2, `resolve(${params.options.rootDir}, ${errorFileName})`)
+    expect(fs.existsSync).nthCalledWith(3, `resolve(join(${params.options.buildDir}, dist, server), ${errorFileName})`)
+    expect(fs.existsSync).nthCalledWith(4, `resolve(${params.options.buildDir}, ${errorFileName})`)
+    expect(fs.existsSync).nthCalledWith(5, `resolve(${process.cwd()}, ${errorFileName})`)
   })
 
   test('should return source content after read source', async () => {
@@ -204,20 +174,20 @@ describe('server: errorMiddleware', () => {
 
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
-    const frame = { fileName: 'webpack:///test-error.js?desc=test' }
+    const frame = { fileName: errorFileName }
     const readSource = Youch.mock.calls[0][2]
+    fs.existsSync.mockImplementationOnce(() => true)
     fs.readFile.mockImplementationOnce(() => Promise.resolve('source content'))
     await readSource(frame)
 
-    const fileName = 'test-error.js'
     expect(frame).toEqual({
-      fileName,
+      fileName: errorFileName,
       contents: 'source content',
-      fullPath: `resolve(${params.options.srcDir}, ${fileName})`
+      fullPath: errorFileName
     })
   })
 
-  test('should return relative fileName if fileName is absolute path', async () => {
+  test('should ignore if source file not exists', async () => {
     const params = createParams()
     params.options.debug = true
     const errorMiddleware = createErrorMiddleware(params)
@@ -226,39 +196,11 @@ describe('server: errorMiddleware', () => {
 
     await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
 
-    const frame = { fileName: 'webpack:///test-error.js?desc=test' }
+    const frame = { fileName: errorFileName }
     const readSource = Youch.mock.calls[0][2]
-    fs.readFile.mockImplementationOnce(() => Promise.resolve('source content'))
-    path.isAbsolute.mockReturnValueOnce(true)
-    path.relative.mockImplementationOnce((...args) => `relative(${args.join(', ')})`)
+    fs.exists.mockReturnValueOnce(false)
     await readSource(frame)
-
-    const fullPath = `resolve(${params.options.srcDir}, test-error.js)`
-    expect(frame).toEqual({
-      fileName: `relative(${params.options.rootDir}, ${fullPath})`,
-      contents: 'source content',
-      fullPath
-    })
-  })
-
-  test('should ignore error when reading source', async () => {
-    const params = createParams()
-    params.options.debug = true
-    const errorMiddleware = createErrorMiddleware(params)
-    const error = {}
-    const ctx = createServerContext()
-
-    await errorMiddleware(error, ctx.req, ctx.res, ctx.next)
-
-    const frame = { fileName: 'webpack:///test-error.js?desc=test' }
-    const readSource = Youch.mock.calls[0][2]
-    fs.readFile.mockReturnValueOnce(Promise.reject(new Error('read failed')))
-    await readSource(frame)
-
-    const fileName = 'test-error.js'
-    expect(frame).toEqual({ fileName })
-    expect(path.resolve).toBeCalledTimes(5)
-    expect(fs.readFile).toBeCalledTimes(5)
+    expect(frame).toEqual({ fileName: errorFileName })
   })
 
   test('should return if fileName is unknown when read source', async () => {
@@ -274,6 +216,6 @@ describe('server: errorMiddleware', () => {
     const readSource = Youch.mock.calls[0][2]
     await readSource(frame)
 
-    expect(frame.fileName).toBeNull()
+    expect(frame.fileName).toBeUndefined()
   })
 })
diff --git a/test/dev/with-config.test.js b/test/dev/with-config.test.js
index 97b1445e3..687f8b913 100644
--- a/test/dev/with-config.test.js
+++ b/test/dev/with-config.test.js
@@ -48,7 +48,7 @@ describe('with-config', () => {
   test('/ (preload fonts)', async () => {
     const { html } = await nuxt.server.renderRoute('/')
     expect(html).toContain(
-      '<link rel="preload" href="/test/orion/fonts/7cf5d7c.woff2" as="font" type="font/woff2" crossorigin'
+      '<link rel="preload" href="/test/orion/fonts/roboto.7cf5d7c.woff2" as="font" type="font/woff2" crossorigin'
     )
   })