Repository: nuxt/nuxt.js
Pull request #7160 (2020-03-31 10:33:24)

v2.12.2
### 🐛 Bug Fixes

- `server`
  - #7146 HMR for sub-app `serverMiddleware` without path
- `vue-app`
  - #7139 Sanitize layouts after resolve for `splitChunks.layout`
- `core`
  - #7131 Join with os native sep in the resolver


Patch:
diff --git a/distributions/nuxt-start/package.json b/distributions/nuxt-start/package.json
index 987c4f4aa..e450ff75a 100644
--- a/distributions/nuxt-start/package.json
+++ b/distributions/nuxt-start/package.json
@@ -1,6 +1,6 @@
 {
   "name": "nuxt-start",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "description": "Starts Nuxt.js Application in production mode",
   "keywords": [
     "nuxt",
@@ -55,8 +55,8 @@
     "dist"
   ],
   "dependencies": {
-    "@nuxt/cli": "2.12.1",
-    "@nuxt/core": "2.12.1",
+    "@nuxt/cli": "2.12.2",
+    "@nuxt/core": "2.12.2",
     "node-fetch": "^2.6.0",
     "vue": "^2.6.11",
     "vue-client-only": "^2.0.0",
diff --git a/distributions/nuxt/package.json b/distributions/nuxt/package.json
index e22733bd8..6e3d2bc38 100644
--- a/distributions/nuxt/package.json
+++ b/distributions/nuxt/package.json
@@ -1,6 +1,6 @@
 {
   "name": "nuxt",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "description": "A minimalistic framework for server-rendered Vue.js applications (inspired by Next.js)",
   "keywords": [
     "nuxt",
@@ -58,13 +58,13 @@
     "postinstall": "opencollective || exit 0"
   },
   "dependencies": {
-    "@nuxt/builder": "2.12.1",
-    "@nuxt/cli": "2.12.1",
-    "@nuxt/core": "2.12.1",
-    "@nuxt/generator": "2.12.1",
+    "@nuxt/builder": "2.12.2",
+    "@nuxt/cli": "2.12.2",
+    "@nuxt/core": "2.12.2",
+    "@nuxt/generator": "2.12.2",
     "@nuxt/loading-screen": "^1.2.0",
     "@nuxt/opencollective": "^0.3.0",
-    "@nuxt/webpack": "2.12.1"
+    "@nuxt/webpack": "2.12.2"
   },
   "engines": {
     "node": ">=8.9.0",
diff --git a/lerna.json b/lerna.json
index b3c25579d..6d54f6a63 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
 {
-  "version": "2.12.1",
+  "version": "2.12.2",
   "npmClient": "yarn",
   "useWorkspaces": true,
   "conventionalCommits": true,
diff --git a/packages/babel-preset-app/package.json b/packages/babel-preset-app/package.json
index 47eddcff3..13a22fadb 100644
--- a/packages/babel-preset-app/package.json
+++ b/packages/babel-preset-app/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/babel-preset-app",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "description": "babel-preset-app for nuxt.js",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 6ad206936..053deb997 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/builder",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/builder.js",
@@ -9,9 +9,9 @@
   ],
   "dependencies": {
     "@nuxt/devalue": "^1.2.4",
-    "@nuxt/utils": "2.12.1",
-    "@nuxt/vue-app": "2.12.1",
-    "@nuxt/webpack": "2.12.1",
+    "@nuxt/utils": "2.12.2",
+    "@nuxt/vue-app": "2.12.2",
+    "@nuxt/webpack": "2.12.2",
     "chalk": "^3.0.0",
     "chokidar": "^3.3.1",
     "consola": "^2.11.3",
diff --git a/packages/builder/src/builder.js b/packages/builder/src/builder.js
index b27f8eed1..7de154287 100644
--- a/packages/builder/src/builder.js
+++ b/packages/builder/src/builder.js
@@ -742,7 +742,7 @@ export default class Builder {
           }
 
           // Log
-          consola.info(`[HMR] ${chalk.cyan(newItem.route)} (${chalk.grey(fileName)})`)
+          consola.info(`[HMR] ${chalk.cyan(newItem.route || '/')} (${chalk.grey(fileName)})`)
         }
         // Tree may be changed so recreate watcher
         this.serverMiddlewareHMR()
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 768a93237..512aee878 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/cli",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/cli.js",
@@ -12,8 +12,8 @@
     "dist"
   ],
   "dependencies": {
-    "@nuxt/config": "2.12.1",
-    "@nuxt/utils": "2.12.1",
+    "@nuxt/config": "2.12.2",
+    "@nuxt/utils": "2.12.2",
     "boxen": "^4.2.0",
     "chalk": "^3.0.0",
     "consola": "^2.11.3",
diff --git a/packages/config/package.json b/packages/config/package.json
index 9328f09f1..356f2e75c 100644
--- a/packages/config/package.json
+++ b/packages/config/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/config",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/config.js",
@@ -10,7 +10,7 @@
     "index.d.ts"
   ],
   "dependencies": {
-    "@nuxt/utils": "2.12.1",
+    "@nuxt/utils": "2.12.2",
     "consola": "^2.11.3",
     "esm": "^3.2.25",
     "std-env": "^2.2.1"
diff --git a/packages/core/package.json b/packages/core/package.json
index 3279fa003..dead2889c 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/core",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/core.js",
@@ -8,11 +8,11 @@
     "dist"
   ],
   "dependencies": {
-    "@nuxt/config": "2.12.1",
+    "@nuxt/config": "2.12.2",
     "@nuxt/devalue": "^1.2.4",
-    "@nuxt/server": "2.12.1",
-    "@nuxt/utils": "2.12.1",
-    "@nuxt/vue-renderer": "2.12.1",
+    "@nuxt/server": "2.12.2",
+    "@nuxt/utils": "2.12.2",
+    "@nuxt/vue-renderer": "2.12.2",
     "consola": "^2.11.3",
     "debug": "^4.1.1",
     "esm": "^3.2.25",
diff --git a/packages/core/src/resolver.js b/packages/core/src/resolver.js
index bcded60ef..087dacaee 100644
--- a/packages/core/src/resolver.js
+++ b/packages/core/src/resolver.js
@@ -105,8 +105,9 @@ export default class Resolver {
         return resolvedPath + '.' + ext
       }
 
-      if (isDirectory && fs.existsSync(resolvedPath + '/index.' + ext)) {
-        return resolvedPath + '/index.' + ext
+      const resolvedPathwithIndex = join(resolvedPath, 'index.' + ext)
+      if (isDirectory && fs.existsSync(resolvedPathwithIndex)) {
+        return resolvedPathwithIndex
       }
     }
 
diff --git a/packages/generator/package.json b/packages/generator/package.json
index ba1e2707b..465f78213 100644
--- a/packages/generator/package.json
+++ b/packages/generator/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/generator",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/generator.js",
@@ -8,7 +8,7 @@
     "dist"
   ],
   "dependencies": {
-    "@nuxt/utils": "2.12.1",
+    "@nuxt/utils": "2.12.2",
     "chalk": "^3.0.0",
     "consola": "^2.11.3",
     "fs-extra": "^8.1.0",
diff --git a/packages/server/package.json b/packages/server/package.json
index d3af57dd6..0f0f1b7c5 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/server",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/server.js",
@@ -8,9 +8,9 @@
     "dist"
   ],
   "dependencies": {
-    "@nuxt/config": "2.12.1",
-    "@nuxt/utils": "2.12.1",
-    "@nuxt/vue-renderer": "2.12.1",
+    "@nuxt/config": "2.12.2",
+    "@nuxt/utils": "2.12.2",
+    "@nuxt/vue-renderer": "2.12.2",
     "@nuxtjs/youch": "^4.2.3",
     "chalk": "^3.0.0",
     "compression": "^1.7.4",
diff --git a/packages/server/src/server.js b/packages/server/src/server.js
index 06afdccfb..cd5cebfc3 100644
--- a/packages/server/src/server.js
+++ b/packages/server/src/server.js
@@ -260,6 +260,11 @@ export default class Server {
       (typeof middleware.route === 'string' ? middleware.route : '')
     ).replace(/\/\//g, '/')
 
+    // Strip trailing slash
+    if (middleware.route.endsWith('/')) {
+      middleware.route = middleware.route.slice(0, -1)
+    }
+
     // Assign _middleware to handle to make accessable from app.stack
     middleware.handle._middleware = middleware
 
@@ -294,11 +299,7 @@ export default class Server {
     serverStackItem.handle = handle
 
     // Error State
-    if (route.includes('#error')) {
-      serverStackItem.route = serverStackItem.route || '/'
-    } else {
-      serverStackItem.route = route
-    }
+    serverStackItem.route = route
 
     // Return updated item
     return serverStackItem
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 18e9c67e0..c287e9932 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/utils",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/utils.js",
diff --git a/packages/vue-app/package.json b/packages/vue-app/package.json
index 91577ea5e..b87e7d3f3 100644
--- a/packages/vue-app/package.json
+++ b/packages/vue-app/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/vue-app",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/vue-app.js",
diff --git a/packages/vue-app/template/App.js b/packages/vue-app/template/App.js
index 2838dac26..ce304f644 100644
--- a/packages/vue-app/template/App.js
+++ b/packages/vue-app/template/App.js
@@ -18,15 +18,19 @@ import '<%= relativeToBuild(resolvePath(c.src || c, { isStyle: true })) %>'
 <% if (features.layouts) { %>
 <%= Object.keys(layouts).map((key) => {
   if (splitChunks.layouts) {
-    return `const _${hash(key)} = () => import('${layouts[key]}'  /* webpackChunkName: "${wChunk('layouts/' + key)}" */).then(m => m.default || m)`
+    return `const _${hash(key)} = () => import('${layouts[key]}'  /* webpackChunkName: "${wChunk('layouts/' + key)}" */).then(m => sanitizeComponent(m.default || m))`
   } else {
     return `import _${hash(key)} from '${layouts[key]}'`
   }
 }).join('\n') %>
 
+<% if (splitChunks.layouts) { %>
+let resolvedLayouts = {}
+const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`).join(',') %> }<%= isTest ? '// eslint-disable-line' : '' %>
+<% } else { %>
 const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": sanitizeComponent(_${hash(key)})`).join(',') %> }<%= isTest ? '// eslint-disable-line' : '' %>
+<% } %>
 
-<% if (splitChunks.layouts) { %>let resolvedLayouts = {}<% } %>
 <% } %>
 
 export default {
diff --git a/packages/vue-renderer/package.json b/packages/vue-renderer/package.json
index 2515e4f38..01fb3702d 100644
--- a/packages/vue-renderer/package.json
+++ b/packages/vue-renderer/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/vue-renderer",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/vue-renderer.js",
@@ -9,7 +9,7 @@
   ],
   "dependencies": {
     "@nuxt/devalue": "^1.2.4",
-    "@nuxt/utils": "2.12.1",
+    "@nuxt/utils": "2.12.2",
     "consola": "^2.11.3",
     "fs-extra": "^8.1.0",
     "lru-cache": "^5.1.1",
diff --git a/packages/webpack/package.json b/packages/webpack/package.json
index 14054adc8..c85126742 100644
--- a/packages/webpack/package.json
+++ b/packages/webpack/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@nuxt/webpack",
-  "version": "2.12.1",
+  "version": "2.12.2",
   "repository": "nuxt/nuxt.js",
   "license": "MIT",
   "main": "dist/webpack.js",
@@ -9,9 +9,9 @@
   ],
   "dependencies": {
     "@babel/core": "^7.9.0",
-    "@nuxt/babel-preset-app": "2.12.1",
+    "@nuxt/babel-preset-app": "2.12.2",
     "@nuxt/friendly-errors-webpack-plugin": "^2.5.0",
-    "@nuxt/utils": "2.12.1",
+    "@nuxt/utils": "2.12.2",
     "babel-loader": "^8.1.0",
     "cache-loader": "^4.1.0",
     "caniuse-lite": "^1.0.30001036",


Test patch:
diff --git a/packages/core/test/resolver.test.js b/packages/core/test/resolver.test.js
index 4b0deb448..bf89d201a 100644
--- a/packages/core/test/resolver.test.js
+++ b/packages/core/test/resolver.test.js
@@ -6,12 +6,14 @@ import { startsWithRootAlias, startsWithSrcAlias } from '@nuxt/utils'
 
 import Resolver from '../src/resolver'
 
-jest.mock('path')
 jest.mock('esm', () => jest.fn(() => jest.fn()))
 jest.mock('fs-extra')
 jest.mock('@nuxt/utils')
 
-describe('core: resolver', () => {
+jest.spyOn(path, 'join')
+jest.spyOn(path, 'resolve')
+
+describe.posix('core: resolver', () => {
   beforeEach(() => {
     jest.clearAllMocks()
   })
@@ -107,7 +109,7 @@ describe('core: resolver', () => {
     startsWithRootAlias.mockReturnValue(false)
     startsWithSrcAlias.mockReturnValue(false)
 
-    const aliasPath = { substr: jest.fn(p => String(p)) }
+    const aliasPath = 'x'
     resolver.resolveAlias(aliasPath)
 
     expect(path.resolve).toBeCalledTimes(1)
diff --git a/packages/server/test/server.test.js b/packages/server/test/server.test.js
index 18f10ae1c..798e23f20 100644
--- a/packages/server/test/server.test.js
+++ b/packages/server/test/server.test.js
@@ -333,7 +333,7 @@ describe('server: server', () => {
 
   test('should use object middleware', () => {
     const nuxt = createNuxt()
-    nuxt.options.router = { base: '/' }
+    nuxt.options.router = { base: '' }
     const server = new Server(nuxt)
     const handler = jest.fn()
 
@@ -348,7 +348,7 @@ describe('server: server', () => {
 
   test('should use function module middleware', () => {
     const nuxt = createNuxt()
-    nuxt.options.router = { base: '/' }
+    nuxt.options.router = { base: '' }
     const server = new Server(nuxt)
     const handler = jest.fn()
     nuxt.resolver.requireModule.mockReturnValueOnce(handler)
diff --git a/test/fixtures/basic/nuxt.config.js b/test/fixtures/basic/nuxt.config.js
index 5e47058d4..652c45d19 100644
--- a/test/fixtures/basic/nuxt.config.js
+++ b/test/fixtures/basic/nuxt.config.js
@@ -81,6 +81,9 @@ export default {
     scopeHoisting: true,
     publicPath: '',
     followSymlinks: true,
+    splitChunks: {
+      layouts: true
+    },
     postcss: {
       preset: {
         features: {