mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-26 23:52:06 +00:00
refactor: move modern detection from server to utils (#5584)
This commit is contained in:
parent
912ef25fce
commit
31a15559e5
@ -22,11 +22,9 @@
|
||||
"launch-editor-middleware": "^2.2.1",
|
||||
"on-headers": "^1.0.2",
|
||||
"pify": "^4.0.1",
|
||||
"semver": "^6.0.0",
|
||||
"serve-placeholder": "^1.2.1",
|
||||
"serve-static": "^1.13.2",
|
||||
"server-destroy": "^1.0.1",
|
||||
"ua-parser-js": "^0.7.19"
|
||||
"server-destroy": "^1.0.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -1,13 +0,0 @@
|
||||
export default {
|
||||
Edge: '16',
|
||||
Firefox: '60',
|
||||
Chrome: '61',
|
||||
'Chrome Headless': '61',
|
||||
Chromium: '61',
|
||||
Iron: '61',
|
||||
Safari: '10.1',
|
||||
Opera: '48',
|
||||
Yandex: '18',
|
||||
Vivaldi: '1.14',
|
||||
'Mobile Safari': '10.3'
|
||||
}
|
@ -11,7 +11,6 @@ import renderAndGetWindow from './jsdom'
|
||||
import nuxtMiddleware from './middleware/nuxt'
|
||||
import errorMiddleware from './middleware/error'
|
||||
import Listener from './listener'
|
||||
import createModernMiddleware from './middleware/modern'
|
||||
import createTimingMiddleware from './middleware/timing'
|
||||
|
||||
export default class Server {
|
||||
@ -113,11 +112,6 @@ export default class Server {
|
||||
|
||||
// Dev middleware
|
||||
if (this.options.dev) {
|
||||
// devMiddleware needs req._modern for serving different files
|
||||
this.useMiddleware(createModernMiddleware({
|
||||
serverContext: this.serverContext
|
||||
}))
|
||||
|
||||
this.useMiddleware((req, res, next) => {
|
||||
if (!this.devMiddleware) {
|
||||
return next()
|
||||
@ -159,13 +153,6 @@ export default class Server {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.dev) {
|
||||
// Put detection after serve-static for avoiding unnecessary detections
|
||||
this.useMiddleware(createModernMiddleware({
|
||||
serverContext: this.serverContext
|
||||
}))
|
||||
}
|
||||
|
||||
// Finally use nuxtMiddleware
|
||||
this.useMiddleware(nuxtMiddleware({
|
||||
options: this.options,
|
||||
|
@ -1,107 +0,0 @@
|
||||
jest.mock('chalk', () => ({
|
||||
green: {
|
||||
bold: modern => `greenBold(${modern})`
|
||||
}
|
||||
}))
|
||||
|
||||
const createServerContext = () => ({
|
||||
resources: {},
|
||||
options: {
|
||||
render: {}
|
||||
}
|
||||
})
|
||||
|
||||
const createRenderContext = () => ({
|
||||
req: { headers: {} },
|
||||
next: jest.fn()
|
||||
})
|
||||
|
||||
describe('server: modernMiddleware', () => {
|
||||
let createModernMiddleware
|
||||
|
||||
beforeEach(() => {
|
||||
jest.isolateModules(() => {
|
||||
createModernMiddleware = require('../../src/middleware/modern').default
|
||||
})
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
test('should return modern middleware', () => {
|
||||
const modernMiddleware = createModernMiddleware({})
|
||||
expect(modernMiddleware).toBeInstanceOf(Function)
|
||||
})
|
||||
|
||||
test('should not detect modern build if modern mode is specified', () => {
|
||||
const serverContext = createServerContext()
|
||||
const modernMiddleware = createModernMiddleware({ serverContext })
|
||||
const ctx = createRenderContext()
|
||||
|
||||
serverContext.options.modern = false
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
serverContext.options.modern = 'client'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
serverContext.options.modern = 'server'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.req._modern).toEqual(false)
|
||||
})
|
||||
|
||||
test('should not detect modern browser if connect has been detected', () => {
|
||||
const serverContext = createServerContext()
|
||||
const modernMiddleware = createModernMiddleware({ serverContext })
|
||||
const ctx = createRenderContext()
|
||||
ctx.req.socket = { isModernBrowser: true }
|
||||
|
||||
serverContext.options.dev = true
|
||||
serverContext.options.modern = 'server'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.req._modern).toEqual(true)
|
||||
})
|
||||
|
||||
test('should detect modern browser based on user-agent', () => {
|
||||
const serverContext = createServerContext()
|
||||
const modernMiddleware = createModernMiddleware({ serverContext })
|
||||
const ctx = createRenderContext()
|
||||
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
|
||||
ctx.req.headers['user-agent'] = ua
|
||||
ctx.req.socket = {}
|
||||
|
||||
serverContext.options.dev = true
|
||||
serverContext.options.modern = 'server'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.req.socket.isModernBrowser).toEqual(true)
|
||||
expect(ctx.req._modern).toEqual(true)
|
||||
})
|
||||
|
||||
test('should detect legacy browser based on user-agent', () => {
|
||||
const serverContext = createServerContext()
|
||||
const modernMiddleware = createModernMiddleware({ serverContext })
|
||||
const ctx = createRenderContext()
|
||||
const ua = 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'
|
||||
ctx.req.headers['user-agent'] = ua
|
||||
ctx.req.socket = {}
|
||||
|
||||
serverContext.options.dev = true
|
||||
serverContext.options.modern = 'client'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.req.socket.isModernBrowser).toEqual(false)
|
||||
})
|
||||
|
||||
test('should ignore illegal user-agent', () => {
|
||||
const serverContext = createServerContext()
|
||||
const modernMiddleware = createModernMiddleware({ serverContext })
|
||||
const ctx = createRenderContext()
|
||||
const ua = 'illegal user agent'
|
||||
ctx.req.headers['user-agent'] = ua
|
||||
ctx.req.socket = {}
|
||||
|
||||
serverContext.options.dev = true
|
||||
serverContext.options.modern = 'client'
|
||||
modernMiddleware(ctx.req, ctx.res, ctx.next)
|
||||
|
||||
expect(ctx.req.socket.isModernBrowser).toEqual(false)
|
||||
})
|
||||
})
|
@ -13,7 +13,6 @@ import ServerContext from '../src/context'
|
||||
import renderAndGetWindow from '../src/jsdom'
|
||||
import nuxtMiddleware from '../src/middleware/nuxt'
|
||||
import errorMiddleware from '../src/middleware/error'
|
||||
import createModernMiddleware from '../src/middleware/modern'
|
||||
import createTimingMiddleware from '../src/middleware/timing'
|
||||
|
||||
jest.mock('connect')
|
||||
@ -27,7 +26,6 @@ jest.mock('../src/context')
|
||||
jest.mock('../src/jsdom')
|
||||
jest.mock('../src/middleware/nuxt')
|
||||
jest.mock('../src/middleware/error')
|
||||
jest.mock('../src/middleware/modern')
|
||||
jest.mock('../src/middleware/timing')
|
||||
|
||||
describe('server: server', () => {
|
||||
@ -72,10 +70,6 @@ describe('server: server', () => {
|
||||
jest.spyOn(path, 'resolve').mockImplementation((...args) => `resolve(${args.join(', ')})`)
|
||||
connect.mockReturnValue({ use: jest.fn() })
|
||||
serveStatic.mockImplementation(dir => ({ id: 'test-serve-static', dir }))
|
||||
createModernMiddleware.mockImplementation(options => ({
|
||||
id: 'test-modern-middleware',
|
||||
...options
|
||||
}))
|
||||
nuxtMiddleware.mockImplementation(options => ({
|
||||
id: 'test-nuxt-middleware',
|
||||
...options
|
||||
@ -178,7 +172,7 @@ describe('server: server', () => {
|
||||
expect(server.nuxt.callHook).nthCalledWith(1, 'render:setupMiddleware', server.app)
|
||||
expect(server.nuxt.callHook).nthCalledWith(2, 'render:errorMiddleware', server.app)
|
||||
|
||||
expect(server.useMiddleware).toBeCalledTimes(5)
|
||||
expect(server.useMiddleware).toBeCalledTimes(4)
|
||||
expect(serveStatic).toBeCalledTimes(2)
|
||||
expect(serveStatic).nthCalledWith(1, 'resolve(/var/nuxt/src, var/nuxt/static)', server.options.render.static)
|
||||
expect(server.useMiddleware).nthCalledWith(1, {
|
||||
@ -195,17 +189,6 @@ describe('server: server', () => {
|
||||
path: '__nuxt_test'
|
||||
})
|
||||
|
||||
const modernMiddleware = {
|
||||
serverContext: server.serverContext
|
||||
}
|
||||
|
||||
expect(createModernMiddleware).toBeCalledTimes(1)
|
||||
expect(createModernMiddleware).toBeCalledWith(modernMiddleware)
|
||||
expect(server.useMiddleware).nthCalledWith(3, {
|
||||
id: 'test-modern-middleware',
|
||||
...modernMiddleware
|
||||
})
|
||||
|
||||
const nuxtMiddlewareOpts = {
|
||||
options: server.options,
|
||||
nuxt: server.nuxt,
|
||||
@ -214,7 +197,7 @@ describe('server: server', () => {
|
||||
}
|
||||
expect(nuxtMiddleware).toBeCalledTimes(1)
|
||||
expect(nuxtMiddleware).toBeCalledWith(nuxtMiddlewareOpts)
|
||||
expect(server.useMiddleware).nthCalledWith(4, {
|
||||
expect(server.useMiddleware).nthCalledWith(3, {
|
||||
id: 'test-nuxt-middleware',
|
||||
...nuxtMiddlewareOpts
|
||||
})
|
||||
@ -225,7 +208,7 @@ describe('server: server', () => {
|
||||
}
|
||||
expect(errorMiddleware).toBeCalledTimes(1)
|
||||
expect(errorMiddleware).toBeCalledWith(errorMiddlewareOpts)
|
||||
expect(server.useMiddleware).nthCalledWith(5, {
|
||||
expect(server.useMiddleware).nthCalledWith(4, {
|
||||
id: 'test-error-middleware',
|
||||
...errorMiddlewareOpts
|
||||
})
|
||||
@ -288,7 +271,7 @@ describe('server: server', () => {
|
||||
expect(launchMiddleware).toBeCalledTimes(1)
|
||||
expect(launchMiddleware).toBeCalledWith({ id: 'test-editor' })
|
||||
|
||||
expect(server.useMiddleware).nthCalledWith(4, {
|
||||
expect(server.useMiddleware).nthCalledWith(3, {
|
||||
handler: { id: 'test-editor' },
|
||||
path: '__open-in-editor'
|
||||
})
|
||||
|
@ -12,8 +12,10 @@
|
||||
"fs-extra": "^7.0.1",
|
||||
"hash-sum": "^1.0.2",
|
||||
"proper-lockfile": "^4.1.1",
|
||||
"semver": "^6.0.0",
|
||||
"serialize-javascript": "^1.7.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
"signal-exit": "^3.0.2",
|
||||
"ua-parser-js": "^0.7.19"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -7,3 +7,4 @@ export * from './serialize'
|
||||
export * from './task'
|
||||
export * from './timer'
|
||||
export * from './cjs'
|
||||
export * from './modern'
|
||||
|
@ -1,7 +1,19 @@
|
||||
import UAParser from 'ua-parser-js'
|
||||
import semver from 'semver'
|
||||
|
||||
import ModernBrowsers from './modern-browsers'
|
||||
export const ModernBrowsers = {
|
||||
Edge: '16',
|
||||
Firefox: '60',
|
||||
Chrome: '61',
|
||||
'Chrome Headless': '61',
|
||||
Chromium: '61',
|
||||
Iron: '61',
|
||||
Safari: '10.1',
|
||||
Opera: '48',
|
||||
Yandex: '18',
|
||||
Vivaldi: '1.14',
|
||||
'Mobile Safari': '10.3'
|
||||
}
|
||||
|
||||
const modernBrowsers = Object.keys(ModernBrowsers)
|
||||
.reduce((allBrowsers, browser) => {
|
||||
@ -9,7 +21,7 @@ const modernBrowsers = Object.keys(ModernBrowsers)
|
||||
return allBrowsers
|
||||
}, {})
|
||||
|
||||
const isModernBrowser = (ua) => {
|
||||
export const isModernBrowser = (ua) => {
|
||||
if (!ua) {
|
||||
return false
|
||||
}
|
||||
@ -21,20 +33,16 @@ const isModernBrowser = (ua) => {
|
||||
return Boolean(modernBrowsers[browser.name] && semver.gte(browserVersion, modernBrowsers[browser.name]))
|
||||
}
|
||||
|
||||
const detectModernBrowser = ({ socket = {}, headers }) => {
|
||||
if (socket.isModernBrowser === undefined) {
|
||||
export const isModernRequest = (req, modernMode = false) => {
|
||||
if (modernMode === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
const { socket = {}, headers } = req
|
||||
if (socket._modern === undefined) {
|
||||
const ua = headers && headers['user-agent']
|
||||
socket.isModernBrowser = isModernBrowser(ua)
|
||||
socket._modern = isModernBrowser(ua)
|
||||
}
|
||||
|
||||
return socket.isModernBrowser
|
||||
}
|
||||
|
||||
export default ({ serverContext }) => {
|
||||
return (req, res, next) => {
|
||||
if (serverContext.options.modern !== false) {
|
||||
req._modern = detectModernBrowser(req)
|
||||
}
|
||||
next()
|
||||
}
|
||||
return socket._modern
|
||||
}
|
@ -8,6 +8,7 @@ import * as serialize from '../src/serialize'
|
||||
import * as task from '../src/task'
|
||||
import * as timer from '../src/timer'
|
||||
import * as cjs from '../src/cjs'
|
||||
import * as modern from '../src/modern'
|
||||
|
||||
describe('util: entry', () => {
|
||||
test('should export all methods from utils folder', () => {
|
||||
@ -20,7 +21,8 @@ describe('util: entry', () => {
|
||||
...serialize,
|
||||
...task,
|
||||
...timer,
|
||||
...cjs
|
||||
...cjs,
|
||||
...modern
|
||||
})
|
||||
})
|
||||
})
|
||||
|
60
packages/utils/test/modern.test.js
Normal file
60
packages/utils/test/modern.test.js
Normal file
@ -0,0 +1,60 @@
|
||||
import { isModernRequest } from '../src/modern'
|
||||
|
||||
const createRequest = () => ({
|
||||
socket: {}, headers: {}
|
||||
})
|
||||
|
||||
describe('utils: modern', () => {
|
||||
test('should not detect modern build if modern mode is specified', () => {
|
||||
const req = createRequest()
|
||||
|
||||
isModernRequest(req)
|
||||
isModernRequest(req, 'client')
|
||||
isModernRequest(req, 'server')
|
||||
|
||||
expect(req.socket._modern).toEqual(false)
|
||||
})
|
||||
|
||||
test('should not detect modern browser if connect has been detected', () => {
|
||||
const req = createRequest()
|
||||
req.socket = { _modern: true }
|
||||
|
||||
isModernRequest(req, 'server')
|
||||
|
||||
expect(req.socket._modern).toEqual(true)
|
||||
})
|
||||
|
||||
test('should detect modern browser based on user-agent', () => {
|
||||
const req = createRequest()
|
||||
const ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
|
||||
req.headers['user-agent'] = ua
|
||||
req.socket = {}
|
||||
|
||||
isModernRequest(req, 'server')
|
||||
|
||||
expect(req.socket._modern).toEqual(true)
|
||||
expect(req.socket._modern).toEqual(true)
|
||||
})
|
||||
|
||||
test('should detect legacy browser based on user-agent', () => {
|
||||
const req = createRequest()
|
||||
const ua = 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'
|
||||
req.headers['user-agent'] = ua
|
||||
req.socket = {}
|
||||
|
||||
isModernRequest(req, 'client')
|
||||
|
||||
expect(req.socket._modern).toEqual(false)
|
||||
})
|
||||
|
||||
test('should ignore illegal user-agent', () => {
|
||||
const req = createRequest()
|
||||
const ua = 'illegal user agent'
|
||||
req.headers['user-agent'] = ua
|
||||
req.socket = {}
|
||||
|
||||
isModernRequest(req, 'client')
|
||||
|
||||
expect(req.socket._modern).toEqual(false)
|
||||
})
|
||||
})
|
@ -3,6 +3,7 @@ import fs from 'fs-extra'
|
||||
import chalk from 'chalk'
|
||||
import consola from 'consola'
|
||||
import template from 'lodash/template'
|
||||
import { isModernRequest } from '@nuxt/utils'
|
||||
|
||||
import SPARenderer from './renderers/spa'
|
||||
import SSRRenderer from './renderers/ssr'
|
||||
@ -280,7 +281,8 @@ export default class VueRenderer {
|
||||
|
||||
// renderContext.modern
|
||||
if (renderContext.modern === undefined) {
|
||||
renderContext.modern = req._modern || this.options.modern === 'client'
|
||||
const modernMode = this.options.modern
|
||||
renderContext.modern = modernMode === 'client' || isModernRequest(req, modernMode)
|
||||
}
|
||||
|
||||
// Call renderContext hook
|
||||
|
@ -3,6 +3,7 @@ import Vue from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
import { createRenderer } from 'vue-server-renderer'
|
||||
import LRU from 'lru-cache'
|
||||
import { isModernRequest } from '@nuxt/utils'
|
||||
import BaseRenderer from './base'
|
||||
|
||||
export default class SPARenderer extends BaseRenderer {
|
||||
@ -36,7 +37,8 @@ export default class SPARenderer extends BaseRenderer {
|
||||
|
||||
async render(renderContext) {
|
||||
const { url = '/', req = {}, _generate } = renderContext
|
||||
const modern = req._modern || (this.options.modern && _generate)
|
||||
const modernMode = this.options.modern
|
||||
const modern = (modernMode && _generate) || isModernRequest(req, modernMode)
|
||||
const cacheKey = `${modern ? 'modern:' : 'legacy:'}${url}`
|
||||
let meta = this.cache.get(cacheKey)
|
||||
|
||||
|
@ -6,7 +6,7 @@ import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||
import consola from 'consola'
|
||||
|
||||
import { parallel, sequence, wrapArray } from '@nuxt/utils'
|
||||
import { parallel, sequence, wrapArray, isModernRequest } from '@nuxt/utils'
|
||||
import AsyncMFS from './utils/async-mfs'
|
||||
|
||||
import * as WebpackConfigs from './config'
|
||||
@ -204,7 +204,7 @@ export class WebpackBundler {
|
||||
}
|
||||
|
||||
async middleware(req, res, next) {
|
||||
const name = req._modern ? 'modern' : 'client'
|
||||
const name = isModernRequest(req, this.buildContext.options.modern) ? 'modern' : 'client'
|
||||
|
||||
if (this.devMiddleware && this.devMiddleware[name]) {
|
||||
await this.devMiddleware[name](req, res)
|
||||
|
Loading…
Reference in New Issue
Block a user