diff --git a/packages/config/src/config/_common.js b/packages/config/src/config/_common.js index a5283f10f8..ff73f27ace 100644 --- a/packages/config/src/config/_common.js +++ b/packages/config/src/config/_common.js @@ -12,7 +12,7 @@ export default () => ({ // Mode mode: 'universal', - modern: false, + modern: undefined, // Globals globalName: `nuxt`, diff --git a/packages/config/src/options.js b/packages/config/src/options.js index dc242fbf75..903ab60509 100644 --- a/packages/config/src/options.js +++ b/packages/config/src/options.js @@ -253,10 +253,6 @@ export function getNuxtConfig(_options) { } defaultsDeep(options, modePreset || options.modes.universal) - if (options.modern === true) { - options.modern = 'server' - } - // If no server-side rendering, add appear true transition /* istanbul ignore if */ if (options.render.ssr === false && options.transition) { diff --git a/packages/server/src/middleware/modern.js b/packages/server/src/middleware/modern.js index 46d17fdfa9..a0f3616cb6 100644 --- a/packages/server/src/middleware/modern.js +++ b/packages/server/src/middleware/modern.js @@ -1,3 +1,5 @@ +import chalk from 'chalk' +import consola from 'consola' import { ModernBrowsers } from '@nuxt/common' import { matchesUA } from 'browserslist-useragent' @@ -11,12 +13,33 @@ const isModernBrowser = (ua) => { }) } -export default function (req, res, next) { - const { socket = {}, headers } = req - if (socket.modernMode === undefined) { - const ua = headers && headers['user-agent'] - socket.modernMode = isModernBrowser(ua) +let detected = false + +const detectModernBuild = ({ options, resources }) => { + if (detected === false && ![false, 'client', 'server'].includes(options.modern)) { + detected = true + if (resources.modernManifest) { + options.modern = options.render.ssr ? 'server' : 'client' + consola.info(`Modern bundles are detected. Modern mode (${chalk.green.bold(options.modern)}) is enabled now.`) + } else { + options.modern = false + } } - req.modernMode = socket.modernMode +} + +const detectModernBrowser = (req, options) => { + if (options.modern === 'server') { + const { socket = {}, headers } = req + if (socket.modernMode === undefined) { + const ua = headers && headers['user-agent'] + socket.modernMode = isModernBrowser(ua) + } + req.modernMode = socket.modernMode + } +} + +export default ({ context }) => (req, res, next) => { + detectModernBuild(context) + detectModernBrowser(req, context.options) next() } diff --git a/packages/server/src/server.js b/packages/server/src/server.js index 39abdb9fc7..85725946c2 100644 --- a/packages/server/src/server.js +++ b/packages/server/src/server.js @@ -10,7 +10,7 @@ import renderAndGetWindow from './jsdom' import nuxtMiddleware from './middleware/nuxt' import errorMiddleware from './middleware/error' import Listener from './listener' -import modernMiddleware from './middleware/modern' +import createModernMiddleware from './middleware/modern' export default class Server { constructor(nuxt) { @@ -79,12 +79,13 @@ export default class Server { } } - if (this.options.modern === 'server') { - this.useMiddleware(modernMiddleware) - } + const modernMiddleware = createModernMiddleware({ + context: this.renderer.context + }) // Add webpack middleware support only for development if (this.options.dev) { + this.useMiddleware(modernMiddleware) this.useMiddleware(async (req, res, next) => { const name = req.modernMode ? 'modern' : 'client' if (this.devMiddleware[name]) { @@ -124,6 +125,7 @@ export default class Server { this.options.render.dist ) }) + this.useMiddleware(modernMiddleware) } // Add user provided middleware diff --git a/packages/vue-renderer/src/renderer.js b/packages/vue-renderer/src/renderer.js index e0f7b8c8c9..4a8cf5c3e6 100644 --- a/packages/vue-renderer/src/renderer.js +++ b/packages/vue-renderer/src/renderer.js @@ -223,7 +223,8 @@ export default class VueRenderer { rendererOptions ) - if (this.context.options.modern === 'server') { + if (this.context.resources.modernManifest && + !['client', false].includes(this.context.options.modern)) { this.renderer.modern = createBundleRenderer( this.context.resources.serverBundle, { diff --git a/test/unit/modern.client.test.js b/test/unit/modern.client.test.js index 82a5086249..4c884a3719 100644 --- a/test/unit/modern.client.test.js +++ b/test/unit/modern.client.test.js @@ -3,7 +3,7 @@ import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils' let nuxt, port const url = route => 'http://localhost:' + port + route -describe('modern client mode', () => { +describe('modern client mode (SSR)', () => { beforeAll(async () => { const options = await loadFixture('modern', { modern: 'client' }) nuxt = new Nuxt(options) diff --git a/test/unit/modern.server.test.js b/test/unit/modern.server.test.js index 040e37c6b3..d4e13b23c1 100644 --- a/test/unit/modern.server.test.js +++ b/test/unit/modern.server.test.js @@ -1,17 +1,25 @@ +import chalk from 'chalk' +import consola from 'consola' import { loadFixture, getPort, Nuxt, rp, wChunk } from '../utils' let nuxt, port const url = route => 'http://localhost:' + port + route const modernUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36' +const modernInfo = mode => `Modern bundles are detected. Modern mode (${chalk.green.bold(mode)}) is enabled now.` describe('modern server mode', () => { beforeAll(async () => { - const options = await loadFixture('modern', { modern: 'server' }) + const options = await loadFixture('modern') nuxt = new Nuxt(options) port = await getPort() await nuxt.server.listen(port, 'localhost') }) + test('should detect server modern mode', async () => { + await nuxt.server.renderAndGetWindow(url('/')) + expect(consola.info).toHaveBeenCalledWith(modernInfo('server')) + }) + test('should use legacy resources by default', async () => { const response = await rp(url('/')) expect(response).toContain('/_nuxt/app.js') diff --git a/test/unit/modern.spa.test.js b/test/unit/modern.spa.test.js new file mode 100644 index 0000000000..c60db96396 --- /dev/null +++ b/test/unit/modern.spa.test.js @@ -0,0 +1,53 @@ +import chalk from 'chalk' +import consola from 'consola' +import { loadFixture, getPort, Nuxt, rp } from '../utils' + +let nuxt, port, options +const url = route => 'http://localhost:' + port + route +const modernInfo = mode => `Modern bundles are detected. Modern mode (${chalk.green.bold(mode)}) is enabled now.` + +describe('modern client mode (SPA)', () => { + beforeAll(async () => { + options = await loadFixture('modern', { render: { ssr: false } }) + nuxt = new Nuxt(options) + port = await getPort() + await nuxt.server.listen(port, 'localhost') + }) + + test('should detect client modern mode', async () => { + await nuxt.server.renderAndGetWindow(url('/')) + expect(consola.info).toHaveBeenCalledWith(modernInfo('client')) + }) + + test('should contain nomodule legacy resources', async () => { + const response = await rp(url('/')) + expect(response).toContain('src="/_nuxt/app.js" nomodule') + expect(response).toContain('src="/_nuxt/commons.app.js" nomodule') + }) + + test('should contain module modern resources', async () => { + const response = await rp(url('/')) + expect(response).toContain('