chore: lint vue-app templates (#6390)

This commit is contained in:
Pim 2019-09-10 11:51:14 +02:00 committed by Pooya Parsa
parent 8ccca220ed
commit 8f0f16b076
20 changed files with 329 additions and 226 deletions

View File

@ -36,7 +36,7 @@ module.exports = {
'vue/component-name-in-template-casing': ['warn', 'kebab-case'] 'vue/component-name-in-template-casing': ['warn', 'kebab-case']
} }
}, { }, {
files: [ 'test/fixtures/*/.nuxt*/**/+(App|index|server|client).js' ], files: [ 'test/fixtures/*/.nuxt*/**/+(App|index|server|client|nuxt).js' ],
rules: { rules: {
'import/order': 'off' 'import/order': 'off'
} }

View File

@ -41,7 +41,7 @@
"consola": "^2.10.1", "consola": "^2.10.1",
"cross-spawn": "^7.0.0", "cross-spawn": "^7.0.0",
"eslint": "^6.3.0", "eslint": "^6.3.0",
"eslint-multiplexer": "^1.0.4", "eslint-multiplexer": "^2.0.0",
"esm": "^3.2.25", "esm": "^3.2.25",
"execa": "^2.0.4", "execa": "^2.0.4",
"express": "^4.17.1", "express": "^4.17.1",

View File

@ -28,11 +28,11 @@ const layouts = { <%= Object.keys(layouts).map(key => `"_${key}": _${hash(key)}`
export default { export default {
<% if (features.meta) { %> <% if (features.meta) { %>
<%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %> <%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren, object-shorthand */' : '' %>
head: <%= serializeFunction(head) %>, head: <%= serializeFunction(head) %>,
<%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %> <%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren, object-shorthand */' : '' %>
<% } %> <% } %>
render(h, props) { render (h, props) {
<% if (loading) { %>const loadingEl = h('NuxtLoading', { ref: 'loading' })<% } %> <% if (loading) { %>const loadingEl = h('NuxtLoading', { ref: 'loading' })<% } %>
<% if (features.layouts) { %> <% if (features.layouts) { %>
const layoutEl = h(this.layout || 'nuxt') const layoutEl = h(this.layout || 'nuxt')
@ -53,7 +53,7 @@ export default {
mode: '<%= layoutTransition.mode %>' mode: '<%= layoutTransition.mode %>'
}, },
on: { on: {
beforeEnter(el) { beforeEnter (el) {
// Ensure to trigger scroll event after calling scrollBehavior // Ensure to trigger scroll event after calling scrollBehavior
window.<%= globals.nuxt %>.$nextTick(() => { window.<%= globals.nuxt %>.$nextTick(() => {
window.<%= globals.nuxt %>.$emit('triggerScroll') window.<%= globals.nuxt %>.$emit('triggerScroll')
@ -84,10 +84,10 @@ export default {
<% } %> <% } %>
}), }),
<% } %> <% } %>
beforeCreate() { beforeCreate () {
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt) Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
}, },
created() { created () {
// Add this.$nuxt in child instances // Add this.$nuxt in child instances
Vue.prototype.<%= globals.nuxt %> = this Vue.prototype.<%= globals.nuxt %> = this
// add to window so we can listen when ready // add to window so we can listen when ready
@ -106,7 +106,7 @@ export default {
this.context = this.$options.context this.context = this.$options.context
}, },
<% if (loading) { %> <% if (loading) { %>
mounted() { mounted () {
this.$loading = this.$refs.loading this.$loading = this.$refs.loading
}, },
watch: { watch: {
@ -115,14 +115,15 @@ export default {
<% } %> <% } %>
<% if (features.clientOnline) { %> <% if (features.clientOnline) { %>
computed: { computed: {
isOffline() { isOffline () {
return !this.isOnline return !this.isOnline
} }
}, },
<% } %> <% } %>
methods: { methods: {
<%= isTest ? '/* eslint-disable comma-dangle */' : '' %>
<% if (features.clientOnline) { %> <% if (features.clientOnline) { %>
refreshOnlineStatus() { refreshOnlineStatus () {
if (process.client) { if (process.client) {
if (typeof window.navigator.onLine === 'undefined') { if (typeof window.navigator.onLine === 'undefined') {
// If the browser doesn't support connection status reports // If the browser doesn't support connection status reports
@ -135,7 +136,7 @@ export default {
} }
}, },
<% } %> <% } %>
async refresh() { async refresh () {
<% if (features.asyncData || features.fetch) { %> <% if (features.asyncData || features.fetch) { %>
const pages = getMatchedComponentsInstances(this.$route) const pages = getMatchedComponentsInstances(this.$route)
@ -144,7 +145,7 @@ export default {
} }
<% if (loading) { %>this.$loading.start()<% } %> <% if (loading) { %>this.$loading.start()<% } %>
const promises = pages.map(async (page) => { const promises = pages.map((page) => {
const p = [] const p = []
<% if (features.fetch) { %> <% if (features.fetch) { %>
@ -177,26 +178,34 @@ export default {
<% } %> <% } %>
}, },
<% if (loading) { %> <% if (loading) { %>
errorChanged() { errorChanged () {
if (this.nuxt.err && this.$loading) { if (this.nuxt.err && this.$loading) {
if (this.$loading.fail) this.$loading.fail() if (this.$loading.fail) {
if (this.$loading.finish) this.$loading.finish() this.$loading.fail()
}
if (this.$loading.finish) {
this.$loading.finish()
}
} }
}, },
<% } %> <% } %>
<% if (features.layouts) { %> <% if (features.layouts) { %>
<% if (splitChunks.layouts) { %> <% if (splitChunks.layouts) { %>
setLayout(layout) { setLayout (layout) {
<% if (debug) { %> <% if (debug) { %>
if(layout && typeof layout !== 'string') throw new Error('[nuxt] Avoid using non-string value as layout property.') if(layout && typeof layout !== 'string') {
throw new Error('[nuxt] Avoid using non-string value as layout property.')
}
<% } %> <% } %>
if (!layout || !resolvedLayouts['_' + layout]) layout = 'default' if (!layout || !resolvedLayouts['_' + layout]) {
layout = 'default'
}
this.layoutName = layout this.layoutName = layout
let _layout = '_' + layout let _layout = '_' + layout
this.layout = resolvedLayouts[_layout] this.layout = resolvedLayouts[_layout]
return this.layout return this.layout
}, },
loadLayout(layout) { loadLayout (layout) {
const undef = !layout const undef = !layout
const nonexistent = !(layouts['_' + layout] || resolvedLayouts['_' + layout]) const nonexistent = !(layouts['_' + layout] || resolvedLayouts['_' + layout])
let _layout = '_' + ((undef || nonexistent) ? 'default' : layout) let _layout = '_' + ((undef || nonexistent) ? 'default' : layout)
@ -216,9 +225,11 @@ export default {
}) })
} }
<% } else { %> <% } else { %>
setLayout(layout) { setLayout (layout) {
<% if (debug) { %> <% if (debug) { %>
if(layout && typeof layout !== 'string') throw new Error('[nuxt] Avoid using non-string value as layout property.') if(layout && typeof layout !== 'string') {
throw new Error('[nuxt] Avoid using non-string value as layout property.')
}
<% } %> <% } %>
if (!layout || !layouts['_' + layout]) { if (!layout || !layouts['_' + layout]) {
layout = 'default' layout = 'default'
@ -227,7 +238,7 @@ export default {
this.layout = layouts['_' + layout] this.layout = layouts['_' + layout]
return this.layout return this.layout
}, },
loadLayout(layout) { loadLayout (layout) {
if (!layout || !layouts['_' + layout]) { if (!layout || !layouts['_' + layout]) {
layout = 'default' layout = 'default'
} }
@ -241,4 +252,5 @@ export default {
NuxtLoading NuxtLoading
} }
<% } %> <% } %>
<%= isTest ? '/* eslint-enable comma-dangle */' : '' %>
} }

View File

@ -2,21 +2,21 @@ import Vue from 'vue'
<% if (fetch.client) { %>import fetch from 'unfetch'<% } %> <% if (fetch.client) { %>import fetch from 'unfetch'<% } %>
<% if (features.middleware) { %>import middleware from './middleware.js'<% } %> <% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
import { import {
<% if (features.asyncData) { %>applyAsyncData,<% } %> <% if (features.asyncData) { %>applyAsyncData,
promisify,<% } %>
<% if (features.middleware) { %>middlewareSeries,<% } %> <% if (features.middleware) { %>middlewareSeries,<% } %>
sanitizeComponent, <% if (features.transitions || (features.middleware && features.layouts)) { %>sanitizeComponent,<% } %>
resolveRouteComponents, <% if (loading) { %>resolveRouteComponents,<% } %>
getMatchedComponents, getMatchedComponents,
getMatchedComponentsInstances, getMatchedComponentsInstances,
flatMapComponents, flatMapComponents,
setContext, setContext,
promisify, <% if (features.transitions) { %>getLocation,<% } %>
getLocation,
compile, compile,
getQueryDiff, getQueryDiff,
globalHandleError globalHandleError
} from './utils.js' } from './utils.js'
import { createApp, NuxtError } from './index.js' import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch && router.prefetchLinks ? "client" : "server" %>.js' // should be included after ./index.js import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch && router.prefetchLinks ? "client" : "server" %>.js' // should be included after ./index.js
<% if (isDev) { %>import consola from 'consola'<% } %> <% if (isDev) { %>import consola from 'consola'<% } %>
@ -31,7 +31,7 @@ Vue.component(NuxtLink.name, NuxtLink)
<% if (fetch.client) { %>if (!global.fetch) { global.fetch = fetch }<% } %> <% if (fetch.client) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
// Global shared references // Global shared references
let _lastPaths = [] let _lastPaths = []<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %>
let app let app
let router let router
<% if (store) { %>let store<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %><% } %> <% if (store) { %>let store<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %><% } %>
@ -95,7 +95,7 @@ const errorHandler = Vue.config.errorHandler || console.error
createApp().then(mountApp).catch(errorHandler) createApp().then(mountApp).catch(errorHandler)
<% if (features.transitions) { %> <% if (features.transitions) { %>
function componentOption(component, key, ...args) { function componentOption (component, key, ...args) {
if (!component || !component.options || !component.options[key]) { if (!component || !component.options || !component.options[key]) {
return {} return {}
} }
@ -106,7 +106,7 @@ function componentOption(component, key, ...args) {
return option return option
} }
function mapTransitions(Components, to, from) { function mapTransitions (Components, to, from) {
const componentTransitions = (component) => { const componentTransitions = (component) => {
const transition = componentOption(component, 'transition', to, from) || {} const transition = componentOption(component, 'transition', to, from) || {}
return (typeof transition === 'string' ? { name: transition } : transition) return (typeof transition === 'string' ? { name: transition } : transition)
@ -128,7 +128,7 @@ function mapTransitions(Components, to, from) {
}) })
} }
<% } %> <% } %>
async function loadAsyncComponents(to, from, next) { <% if (loading) { %>async <% } %>function loadAsyncComponents (to, from, next) {
// Check if route path changed (this._pathChanged), only if the page is not an error (for validate()) // Check if route path changed (this._pathChanged), only if the page is not an error (for validate())
this._pathChanged = Boolean(app.nuxt.err) || from.path !== to.path this._pathChanged = Boolean(app.nuxt.err) || from.path !== to.path
this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query) this._queryChanged = JSON.stringify(to.query) !== JSON.stringify(from.query)
@ -141,20 +141,22 @@ async function loadAsyncComponents(to, from, next) {
<% } %> <% } %>
try { try {
const Components = await resolveRouteComponents(
to,
(Component, instance) => ({ Component, instance })
)
<% if (loading) { %> <% if (loading) { %>
if (!this._pathChanged && this._queryChanged) { if (!this._pathChanged && this._queryChanged) {
const Components = await resolveRouteComponents(
to,
(Component, instance) => ({ Component, instance })
)
// Add a marker on each component that it needs to refresh or not // Add a marker on each component that it needs to refresh or not
const startLoader = Components.some(({Component, instance}) => { const startLoader = Components.some(({ Component, instance }) => {
const watchQuery = Component.options.watchQuery const watchQuery = Component.options.watchQuery
if (watchQuery === true) { if (watchQuery === true) {
return true return true
} else if (Array.isArray(watchQuery)) { }
if (Array.isArray(watchQuery)) {
return watchQuery.some(key => this._diffQuery[key]) return watchQuery.some(key => this._diffQuery[key])
} else if (typeof watchQuery === 'function') { }
if (typeof watchQuery === 'function') {
return watchQuery.apply(instance, [to.query, from.query]) return watchQuery.apply(instance, [to.query, from.query])
} }
return false return false
@ -184,7 +186,8 @@ async function loadAsyncComponents(to, from, next) {
} }
} }
function applySSRData(Component, ssrData) { <% if (features.transitions) { %>
function applySSRData (Component, ssrData) {
<% if (features.asyncData) { %> <% if (features.asyncData) { %>
if (NUXT.serverRendered && ssrData) { if (NUXT.serverRendered && ssrData) {
applyAsyncData(Component, ssrData) applyAsyncData(Component, ssrData)
@ -195,7 +198,7 @@ function applySSRData(Component, ssrData) {
} }
// Get matched components // Get matched components
function resolveComponents(router) { function resolveComponents (router) {
const path = getLocation(router.options.base, router.options.mode) const path = getLocation(router.options.base, router.options.mode)
return flatMapComponents(router.match(path), async (Component, _, match, key, index) => { return flatMapComponents(router.match(path), async (Component, _, match, key, index) => {
@ -209,8 +212,10 @@ function resolveComponents(router) {
return _Component return _Component
}) })
} }
<% } %>
<% if (features.middleware) { %> <% if (features.middleware) { %>
function callMiddleware(Components, context, layout) { function callMiddleware (Components, context, layout) {
let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %> let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %>
let unknownMiddleware = false let unknownMiddleware = false
@ -231,7 +236,9 @@ function callMiddleware(Components, context, layout) {
<% } %> <% } %>
midd = midd.map((name) => { midd = midd.map((name) => {
if (typeof name === 'function') return name if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') { if (typeof middleware[name] !== 'function') {
unknownMiddleware = true unknownMiddleware = true
this.error({ statusCode: 500, message: 'Unknown middleware ' + name }) this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
@ -239,22 +246,27 @@ function callMiddleware(Components, context, layout) {
return middleware[name] return middleware[name]
}) })
if (unknownMiddleware) return if (unknownMiddleware) {
return
}
return middlewareSeries(midd, context) return middlewareSeries(midd, context)
} }
<% } else if (isDev) { <% } else if (isDev) {
// This is a placeholder function mainly so we dont have to // This is a placeholder function mainly so we dont have to
// refactor the promise chain in addHotReload() // refactor the promise chain in addHotReload()
%> %>
function callMiddleware() { function callMiddleware () {
return Promise.resolve(true) return Promise.resolve(true)
} }
<% } %> <% } %>
async function render(to, from, next) { async function render (to, from, next) {
if (this._pathChanged === false && this._queryChanged === false) return next() if (this._pathChanged === false && this._queryChanged === false) {
return next()
}
// Handle first render on SPA mode // Handle first render on SPA mode
if (to === from) _lastPaths = [] if (to === from) {
else { _lastPaths = []
} else {
const fromMatches = [] const fromMatches = []
_lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => { _lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => {
return compile(from.matched[fromMatches[i]].path)(from.params) return compile(from.matched[fromMatches[i]].path)(from.params)
@ -274,7 +286,10 @@ async function render(to, from, next) {
this.$loading.pause() this.$loading.pause()
} }
<% } %> <% } %>
if (nextCalled) return if (nextCalled) {
return
}
nextCalled = true nextCalled = true
next(path) next(path)
} }
@ -297,7 +312,9 @@ async function render(to, from, next) {
<% if (features.middleware) { %> <% if (features.middleware) { %>
// Default layout // Default layout
await callMiddleware.call(this, Components, app.context) await callMiddleware.call(this, Components, app.context)
if (nextCalled) return if (nextCalled) {
return
}
<% } %> <% } %>
<% if (features.layouts) { %> <% if (features.layouts) { %>
@ -311,7 +328,9 @@ async function render(to, from, next) {
<% if (features.middleware) { %> <% if (features.middleware) { %>
await callMiddleware.call(this, Components, app.context, layout) await callMiddleware.call(this, Components, app.context, layout)
if (nextCalled) return if (nextCalled) {
return
}
<% } %> <% } %>
// Show error page // Show error page
@ -337,8 +356,12 @@ async function render(to, from, next) {
<% if (features.middleware) { %> <% if (features.middleware) { %>
// Call middleware // Call middleware
await callMiddleware.call(this, Components, app.context) await callMiddleware.call(this, Components, app.context)
if (nextCalled) return if (nextCalled) {
if (app.context._errored) return next() return
}
if (app.context._errored) {
return next()
}
<% } %> <% } %>
<% if (features.layouts) { %> <% if (features.layouts) { %>
@ -353,8 +376,12 @@ async function render(to, from, next) {
<% if (features.middleware) { %> <% if (features.middleware) { %>
// Call middleware for layout // Call middleware for layout
await callMiddleware.call(this, Components, app.context, layout) await callMiddleware.call(this, Components, app.context, layout)
if (nextCalled) return if (nextCalled) {
if (app.context._errored) return next() return
}
if (app.context._errored) {
return next()
}
<% } %> <% } %>
@ -514,7 +541,7 @@ async function render(to, from, next) {
} }
// Fix components format in matched, it's due to code-splitting of vue-router // Fix components format in matched, it's due to code-splitting of vue-router
function normalizeComponents(to, ___) { function normalizeComponents (to, ___) {
flatMapComponents(to, (Component, _, match, key) => { flatMapComponents(to, (Component, _, match, key) => {
if (typeof Component === 'object' && !Component.options) { if (typeof Component === 'object' && !Component.options) {
// Updated via vue-router resolveAsyncComponents() // Updated via vue-router resolveAsyncComponents()
@ -526,7 +553,7 @@ function normalizeComponents(to, ___) {
}) })
} }
function showNextPage(to) { function showNextPage (to) {
// Hide error component if no error // Hide error component if no error
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) { if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
this.error() this.error()
@ -547,15 +574,19 @@ function showNextPage(to) {
// When navigating on a different route but the same component is used, Vue.js // When navigating on a different route but the same component is used, Vue.js
// Will not update the instance data, so we have to update $data ourselves // Will not update the instance data, so we have to update $data ourselves
function fixPrepatch(to, ___) { function fixPrepatch (to, ___) {
if (this._pathChanged === false && this._queryChanged === false) return if (this._pathChanged === false && this._queryChanged === false) {
return
}
const instances = getMatchedComponentsInstances(to) const instances = getMatchedComponentsInstances(to)
const Components = getMatchedComponents(to) const Components = getMatchedComponents(to)
Vue.nextTick(() => { Vue.nextTick(() => {
instances.forEach((instance, i) => { instances.forEach((instance, i) => {
if (!instance || instance._isDestroyed) return if (!instance || instance._isDestroyed) {
return
}
if ( if (
instance.constructor._dataRefresh && instance.constructor._dataRefresh &&
@ -582,7 +613,7 @@ function fixPrepatch(to, ___) {
}) })
} }
function nuxtReady(_app) { function nuxtReady (_app) {
window.<%= globals.readyCallback %>Cbs.forEach((cb) => { window.<%= globals.readyCallback %>Cbs.forEach((cb) => {
if (typeof cb === 'function') { if (typeof cb === 'function') {
cb(_app) cb(_app)
@ -604,7 +635,7 @@ const noopData = () => { return {} }
const noopFetch = () => {} const noopFetch = () => {}
// Special hot reload with asyncData(context) // Special hot reload with asyncData(context)
function getNuxtChildComponents($parent, $components = []) { function getNuxtChildComponents ($parent, $components = []) {
$parent.$children.forEach(($child) => { $parent.$children.forEach(($child) => {
if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) { if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) {
$components.push($child) $components.push($child)
@ -625,7 +656,7 @@ function hotReloadAPI(_app) {
$components.forEach(addHotReload.bind(_app)) $components.forEach(addHotReload.bind(_app))
} }
function addHotReload($component, depth) { function addHotReload ($component, depth) {
if ($component.$vnode.data._hasHotReload) return if ($component.$vnode.data._hasHotReload) return
$component.$vnode.data._hasHotReload = true $component.$vnode.data._hasHotReload = true
@ -721,15 +752,12 @@ function addHotReload($component, depth) {
} }
<% } %> <% } %>
async function mountApp(__app) { <% if (features.layouts || features.transitions) { %>async <% } %>function mountApp (__app) {
// Set global variables // Set global variables
app = __app.app app = __app.app
router = __app.router router = __app.router
<% if (store) { %>store = __app.store<% } %> <% if (store) { %>store = __app.store<% } %>
// Resolve route components
const Components = await Promise.all(resolveComponents(router))
// Create Vue instance // Create Vue instance
const _app = new Vue(app) const _app = new Vue(app)
@ -759,6 +787,9 @@ async function mountApp(__app) {
}) })
} }
<% if (features.transitions) { %> <% if (features.transitions) { %>
// Resolve route components
const Components = await Promise.all(resolveComponents(router))
// Enable transitions // Enable transitions
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app) _app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
if (Components.length) { if (Components.length) {
@ -768,7 +799,9 @@ async function mountApp(__app) {
<% } %> <% } %>
// Initialize error handler // Initialize error handler
_app.$loading = {} // To avoid error while _app.$nuxt does not exist _app.$loading = {} // To avoid error while _app.$nuxt does not exist
if (NUXT.error) _app.error(NUXT.error) if (NUXT.error) {
_app.error(NUXT.error)
}
// Add beforeEach router hooks // Add beforeEach router hooks
router.beforeEach(loadAsyncComponents.bind(_app)) router.beforeEach(loadAsyncComponents.bind(_app))
@ -804,7 +837,9 @@ async function mountApp(__app) {
// Push the path and let route to be resolved // Push the path and let route to be resolved
router.push(path, undefined, (err) => { router.push(path, undefined, (err) => {
if (err) errorHandler(err) if (err) {
errorHandler(err)
}
}) })
}) })
} }

View File

@ -1,12 +1,12 @@
<template> <template>
<transition appear> <transition appear>
<div class="nuxt__build_indicator" :style="indicatorStyle" v-if="building"> <div v-if="building" class="nuxt__build_indicator" :style="indicatorStyle">
<svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg"> <svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd"> <g fill="none" fill-rule="evenodd">
<path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z"/> <path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z" />
<path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E"/> <path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E" />
<path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E"/> <path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E" />
<path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero"/> <path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero" />
</g> </g>
</svg> </svg>
{{ animatedProgress }}% {{ animatedProgress }}%
@ -16,39 +16,29 @@
<script> <script>
export default { export default {
name: 'nuxt-build-indicator', name: 'NuxtBuildIndicator',
data() { data () {
return { return {
building: false, building: false,
progress: 0, progress: 0,
animatedProgress: 0, animatedProgress: 0,
reconnectAttempts: 0, reconnectAttempts: 0
} }
}, },
mounted() {
if (typeof EventSource === undefined) {
return // Unsupported
}
this.sseConnect()
},
beforeDestroy() {
this.sseClose()
clearInterval(this._progressAnimation)
},
computed: { computed: {
options: () => (<%= JSON.stringify(buildIndicator) %>), options: () => (<%= JSON.stringify(buildIndicator) %>),
indicatorStyle() { indicatorStyle () {
const [ d1, d2 ] = this.options.position.split('-') const [ d1, d2 ] = this.options.position.split('-')
return { return {
[d1]: '20px', [d1]: '20px',
[d2]: '20px', [d2]: '20px',
'background-color': this.options.backgroundColor, 'background-color': this.options.backgroundColor,
color: this.options.color, color: this.options.color
} }
} }
}, },
watch: { watch: {
progress(val, oldVal) { progress (val, oldVal) {
// Average progress may decrease but ignore it! // Average progress may decrease but ignore it!
if (val < oldVal) { if (val < oldVal) {
return return
@ -71,18 +61,30 @@ export default {
}, 50) }, 50)
} }
}, },
mounted () {
if (EventSource === undefined) {
return // Unsupported
}
this.sseConnect()
},
beforeDestroy () {
this.sseClose()
clearInterval(this._progressAnimation)
},
methods: { methods: {
sseConnect() { sseConnect () {
if (this._connecting) { if (this._connecting) {
return return
} }
this._connecting = true this._connecting = true
this.sse = new EventSource('<%= router.base %>_loading/sse') this.sse = new EventSource('<%= router.base %>_loading/sse')
this.sse.addEventListener('message', (event) => this.onSseMessage(event)) this.sse.addEventListener('message', event => this.onSseMessage(event))
}, },
onSseMessage(message) { onSseMessage (message) {
const data = JSON.parse(message.data) const data = JSON.parse(message.data)
if (!data.states) return if (!data.states) {
return
}
this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length) this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length)
@ -98,7 +100,7 @@ export default {
} }
}, },
sseClose() { sseClose () {
if (this.sse) { if (this.sse) {
this.sse.close() this.sse.close()
delete this.sse delete this.sse

View File

@ -13,7 +13,7 @@ export default {
default: undefined default: undefined
} }
}, },
render(h, { parent, data, props }) { render (h, { parent, data, props }) {
<% if (features.transitions) { %> <% if (features.transitions) { %>
data.nuxtChild = true data.nuxtChild = true
const _parent = parent const _parent = parent

View File

@ -29,7 +29,7 @@ export default {
default: null default: null
} }
}, },
head() { head () {
return { return {
title: this.message, title: this.message,
meta: [ meta: [
@ -41,10 +41,10 @@ export default {
} }
}, },
computed: { computed: {
statusCode() { statusCode () {
return (this.error && this.error.statusCode) || 500 return (this.error && this.error.statusCode) || 500
}, },
message() { message () {
return this.error.message || `<%= messages.client_error %>` return this.error.message || `<%= messages.client_error %>`
} }
} }

View File

@ -6,9 +6,7 @@ const requestIdleCallback = window.requestIdleCallback ||
return setTimeout(function () { return setTimeout(function () {
cb({ cb({
didTimeout: false, didTimeout: false,
timeRemaining: function () { timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
return Math.max(0, 50 - (Date.now() - start))
}
}) })
}, 1) }, 1)
} }
@ -35,19 +33,19 @@ export default {
default: '<%= router.linkPrefetchedClass %>' default: '<%= router.linkPrefetchedClass %>'
}<% } %> }<% } %>
}, },
mounted() { mounted () {
if (!this.noPrefetch) { if (!this.noPrefetch) {
requestIdleCallback(this.observe, { timeout: 2e3 }) requestIdleCallback(this.observe, { timeout: 2e3 })
} }
}, },
beforeDestroy() { beforeDestroy () {
if (this.__observed) { if (this.__observed) {
observer.unobserve(this.$el) observer.unobserve(this.$el)
delete this.$el.__prefetch delete this.$el.__prefetch
} }
}, },
methods: { methods: {
observe() { observe () {
// If no IntersectionObserver, avoid prefetching // If no IntersectionObserver, avoid prefetching
if (!observer) { if (!observer) {
return return
@ -61,22 +59,22 @@ export default {
this.addPrefetchedClass() this.addPrefetchedClass()
}<% } %> }<% } %>
}, },
shouldPrefetch() { shouldPrefetch () {
return this.getPrefetchComponents().length > 0 return this.getPrefetchComponents().length > 0
}, },
canPrefetch() { canPrefetch () {
const conn = navigator.connection const conn = navigator.connection
const hasBadConnection = this.<%= globals.nuxt %>.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData)) const hasBadConnection = this.<%= globals.nuxt %>.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData))
return !hasBadConnection return !hasBadConnection
}, },
getPrefetchComponents() { getPrefetchComponents () {
const ref = this.$router.resolve(this.to, this.$route, this.append) const ref = this.$router.resolve(this.to, this.$route, this.append)
const Components = ref.resolved.matched.map(r => r.components.default) const Components = ref.resolved.matched.map(r => r.components.default)
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched) return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
}, },
prefetch() { prefetch () {
if (!this.canPrefetch()) { if (!this.canPrefetch()) {
return return
} }
@ -93,7 +91,7 @@ export default {
}<% if (router.linkPrefetchedClass) { %> }<% if (router.linkPrefetchedClass) { %>
this.addPrefetchedClass()<% } %> this.addPrefetchedClass()<% } %>
}<% if (router.linkPrefetchedClass) { %>, }<% if (router.linkPrefetchedClass) { %>,
addPrefetchedClass() { addPrefetchedClass () {
if (this.prefetchedClass !== 'false') { if (this.prefetchedClass !== 'false') {
this.$el.className = (this.$el.className + ' ' + this.prefetchedClass).trim() this.$el.className = (this.$el.className + ' ' + this.prefetchedClass).trim()
} }

View File

@ -1,7 +1,7 @@
<script> <script>
export default { export default {
name: 'NuxtLoading', name: 'NuxtLoading',
data() { data () {
return { return {
percent: 0, percent: 0,
show: false, show: false,
@ -15,7 +15,7 @@ export default {
} }
}, },
computed: { computed: {
left() { left () {
if (!this.continuous && !this.rtl) { if (!this.continuous && !this.rtl) {
return false return false
} }
@ -24,16 +24,16 @@ export default {
: (!this.reversed ? '0px' : 'auto') : (!this.reversed ? '0px' : 'auto')
} }
}, },
beforeDestroy() { beforeDestroy () {
this.clear() this.clear()
}, },
methods: { methods: {
clear() { clear () {
clearInterval(this._timer) clearInterval(this._timer)
clearTimeout(this._throttle) clearTimeout(this._throttle)
this._timer = null this._timer = null
}, },
start() { start () {
this.clear() this.clear()
this.percent = 0 this.percent = 0
this.reversed = false this.reversed = false
@ -47,37 +47,37 @@ export default {
} }
return this return this
}, },
set(num) { set (num) {
this.show = true this.show = true
this.canSucceed = true this.canSucceed = true
this.percent = Math.min(100, Math.max(0, Math.floor(num))) this.percent = Math.min(100, Math.max(0, Math.floor(num)))
return this return this
}, },
get() { get () {
return this.percent return this.percent
}, },
increase(num) { increase (num) {
this.percent = Math.min(100, Math.floor(this.percent + num)) this.percent = Math.min(100, Math.floor(this.percent + num))
return this return this
}, },
decrease(num) { decrease (num) {
this.percent = Math.max(0, Math.floor(this.percent - num)) this.percent = Math.max(0, Math.floor(this.percent - num))
return this return this
}, },
pause() { pause () {
clearInterval(this._timer) clearInterval(this._timer)
return this return this
}, },
resume() { resume () {
this.startTimer() this.startTimer()
return this return this
}, },
finish() { finish () {
this.percent = this.reversed ? 0 : 100 this.percent = this.reversed ? 0 : 100
this.hide() this.hide()
return this return this
}, },
hide() { hide () {
this.clear() this.clear()
setTimeout(() => { setTimeout(() => {
this.show = false this.show = false
@ -88,11 +88,11 @@ export default {
}, 500) }, 500)
return this return this
}, },
fail() { fail () {
this.canSucceed = false this.canSucceed = false
return this return this
}, },
startTimer() { startTimer () {
if (!this.show) { if (!this.show) {
this.show = true this.show = true
} }
@ -133,7 +133,7 @@ export default {
}, 100) }, 100)
} }
}, },
render(h) { render (h) {
let el = h(false) let el = h(false)
if (this.show) { if (this.show) {
el = h('div', { el = h('div', {

View File

@ -44,7 +44,7 @@ export default {
} }
}, },
computed: { computed: {
routerViewKey() { routerViewKey () {
// If nuxtChildKey prop is given or current route has children // If nuxtChildKey prop is given or current route has children
if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) { if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) {
return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params) return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params)
@ -70,10 +70,10 @@ export default {
return strict ? this.$route.path : this.$route.path.replace(/\/$/, '') return strict ? this.$route.path : this.$route.path.replace(/\/$/, '')
} }
}, },
beforeCreate() { beforeCreate () {
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt) Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
}, },
render(h) { render (h) {
// if there is no error // if there is no error
if (!this.nuxt.err) { if (!this.nuxt.err) {
// Directly return nuxt child // Directly return nuxt child

View File

@ -24,9 +24,10 @@ Vue.component(ClientOnly.name, ClientOnly)
// TODO: Remove in Nuxt 3: <NoSsr> // TODO: Remove in Nuxt 3: <NoSsr>
Vue.component(NoSsr.name, { Vue.component(NoSsr.name, {
...NoSsr, ...NoSsr,
render(h, ctx) { render (h, ctx) {
if (process.client && !NoSsr._warned) { if (process.client && !NoSsr._warned) {
NoSsr._warned = true NoSsr._warned = true
<%= isTest ? '// eslint-disable-next-line no-console' : '' %>
console.warn(`<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead`) console.warn(`<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead`)
} }
return NoSsr.render(h, ctx) return NoSsr.render(h, ctx)
@ -62,7 +63,7 @@ const defaultTransition = <%=
%><%= isTest ? '// eslint-disable-line' : '' %> %><%= isTest ? '// eslint-disable-line' : '' %>
<% } %> <% } %>
async function createApp(ssrContext) { async function createApp (ssrContext) {
const router = await createRouter(ssrContext) const router = await createRouter(ssrContext)
<% if (store) { %> <% if (store) { %>
@ -87,7 +88,7 @@ async function createApp(ssrContext) {
<% if (features.transitions) { %> <% if (features.transitions) { %>
defaultTransition, defaultTransition,
transitions: [ defaultTransition ], transitions: [ defaultTransition ],
setTransitions(transitions) { setTransitions (transitions) {
if (!Array.isArray(transitions)) { if (!Array.isArray(transitions)) {
transitions = [ transitions ] transitions = [ transitions ]
} }
@ -107,7 +108,7 @@ async function createApp(ssrContext) {
<% } %> <% } %>
err: null, err: null,
dateErr: null, dateErr: null,
error(err) { error (err) {
err = err || null err = err || null
app.context._errored = Boolean(err) app.context._errored = Boolean(err)
err = err ? normalizeError(err) : null err = err ? normalizeError(err) : null
@ -115,7 +116,9 @@ async function createApp(ssrContext) {
nuxt.dateErr = Date.now() nuxt.dateErr = Date.now()
nuxt.err = err nuxt.err = err
// Used in src/server.js // Used in src/server.js
if (ssrContext) ssrContext.nuxt.error = err if (ssrContext) {
ssrContext.nuxt.error = err
}
return err return err
} }
}, },
@ -150,8 +153,13 @@ async function createApp(ssrContext) {
<% if (plugins.length) { %> <% if (plugins.length) { %>
const inject = function (key, value) { const inject = function (key, value) {
if (!key) throw new Error('inject(key, value) has no key provided') if (!key) {
if (typeof value === 'undefined') throw new Error('inject(key, value) has no value provided') throw new Error('inject(key, value) has no key provided')
}
if (value === undefined) {
throw new Error('inject(key, value) has no value provided')
}
key = '$' + key key = '$' + key
// Add into app // Add into app
app[key] = value app[key] = value
@ -161,13 +169,15 @@ async function createApp(ssrContext) {
<% } %> <% } %>
// Check if plugin not already installed // Check if plugin not already installed
const installKey = '__<%= globals.pluginPrefix %>_' + key + '_installed__' const installKey = '__<%= globals.pluginPrefix %>_' + key + '_installed__'
if (Vue[installKey]) return if (Vue[installKey]) {
return
}
Vue[installKey] = true Vue[installKey] = true
// Call Vue.use() to install the plugin into vm // Call Vue.use() to install the plugin into vm
Vue.use(() => { Vue.use(() => {
if (!Vue.prototype.hasOwnProperty(key)) { if (!Vue.prototype.hasOwnProperty(key)) {
Object.defineProperty(Vue.prototype, key, { Object.defineProperty(Vue.prototype, key, {
get() { get () {
return this.$root.$options[key] return this.$root.$options[key]
} }
}) })

View File

@ -1,11 +1,12 @@
const middleware = {} const middleware = {}
<%= isTest ? '/* eslint-disable dot-notation */' : '' %>
<% for (const m of middleware) { <% for (const m of middleware) {
// TODO: remove duplicate logic in v3 (see builder.resolveMiddleware) // TODO: remove duplicate logic in v3 (see builder.resolveMiddleware)
const name = m.name || m.src.replace(new RegExp(`\\.(${extensions})$`), '') const name = m.name || m.src.replace(new RegExp(`\\.(${extensions})$`), '')
const dst = m.dst || relativeToBuild(srcDir, dir.middleware, m.src) const dst = m.dst || relativeToBuild(srcDir, dir.middleware, m.src)
%> %>
middleware['<%= name %>'] = require('<%= dst %>'); middleware['<%= name %>'] = require('<%= dst %>')
middleware['<%= name %>'] = middleware['<%= name %>'].default || middleware['<%= name %>'] middleware['<%= name %>'] = middleware['<%= name %>'].default || middleware['<%= name %>']
<% } %> <% } %>
<%= isTest ? '/* eslint-enable dot-notation */' : '' %>
export default middleware export default middleware

View File

@ -68,7 +68,7 @@ import scrollBehavior from './router.scrollBehavior.js'
return res return res
} }
const _components = [] const _components = []
const _routes = recursiveRoutes(router.routes, ' ', _components, 2) const _routes = recursiveRoutes(router.routes, ' ', _components, 1)
%><%= uniqBy(_components, '_name').map((route) => { %><%= uniqBy(_components, '_name').map((route) => {
if (!route.component) return '' if (!route.component) return ''
const path = relativeToBuild(route.component) const path = relativeToBuild(route.component)
@ -98,6 +98,6 @@ export const routerOptions = {
fallback: <%= router.fallback %> fallback: <%= router.fallback %>
} }
export function createRouter() { export function createRouter () {
return new Router(routerOptions) return new Router(routerOptions)
} }

View File

@ -1,5 +1,7 @@
<% if (router.scrollBehavior) { %> <% if (router.scrollBehavior) { %>
<%= isTest ? '/* eslint-disable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
export default <%= serializeFunction(router.scrollBehavior) %> export default <%= serializeFunction(router.scrollBehavior) %>
<%= isTest ? '/* eslint-enable quotes, semi, indent, comma-spacing, key-spacing, object-curly-spacing, space-before-function-paren */' : '' %>
<% } else { %>import { getMatchedComponents } from './utils' <% } else { %>import { getMatchedComponents } from './utils'
if (process.client) { if (process.client) {
@ -67,6 +69,7 @@ export default function (to, from, savedPosition) {
position = { selector: hash } position = { selector: hash }
} }
} catch (e) { } catch (e) {
<%= isTest ? '// eslint-disable-next-line no-console' : '' %>
console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).') console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
} }
} }

View File

@ -5,11 +5,11 @@ import Vue from 'vue'
import { import {
<% if (features.asyncData) { %>applyAsyncData,<% } %> <% if (features.asyncData) { %>applyAsyncData,<% } %>
<% if (features.middleware) { %>middlewareSeries,<% } %> <% if (features.middleware) { %>middlewareSeries,<% } %>
<% if (features.middleware && features.layouts) { %>sanitizeComponent,<% } %>
getMatchedComponents, getMatchedComponents,
promisify, promisify
sanitizeComponent
} from './utils.js' } from './utils.js'
import { createApp, NuxtError } from './index.js' import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
// Component: <NuxtLink> // Component: <NuxtLink>
@ -20,7 +20,7 @@ Vue.component(NuxtLink.name, NuxtLink)
const noopApp = () => new Vue({ render: h => h('div') }) const noopApp = () => new Vue({ render: h => h('div') })
function urlJoin() { function urlJoin () {
return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/') return Array.prototype.slice.call(arguments).join('/').replace(/\/+/g, '/')
} }
@ -114,13 +114,17 @@ export default async (ssrContext) => {
try { try {
await store.dispatch('nuxtServerInit', app.context) await store.dispatch('nuxtServerInit', app.context)
} catch (err) { } catch (err) {
console.debug('Error occurred when calling nuxtServerInit: ', err.message) console.debug('Error occurred when calling nuxtServerInit: ', err.message)<%= isTest ? '// eslint-disable-line no-console' : '' %>
throw err throw err
} }
} }
// ...If there is a redirect or an error, stop the process // ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) return noopApp() if (ssrContext.redirected) {
if (ssrContext.nuxt.error) return renderErrorPage() return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
<% } %> <% } %>
<% if (features.middleware) { %> <% if (features.middleware) { %>
@ -129,7 +133,9 @@ export default async (ssrContext) => {
*/ */
let midd = <%= serialize(router.middleware).replace('middleware(', 'function(') %><%= isTest ? '// eslint-disable-line' : '' %> let midd = <%= serialize(router.middleware).replace('middleware(', 'function(') %><%= isTest ? '// eslint-disable-line' : '' %>
midd = midd.map((name) => { midd = midd.map((name) => {
if (typeof name === 'function') return name if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') { if (typeof middleware[name] !== 'function') {
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name }) app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
} }
@ -137,8 +143,12 @@ export default async (ssrContext) => {
}) })
await middlewareSeries(midd, app.context) await middlewareSeries(midd, app.context)
// ...If there is a redirect or an error, stop the process // ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) return noopApp() if (ssrContext.redirected) {
if (ssrContext.nuxt.error) return renderErrorPage() return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
<% } %> <% } %>
<% if (features.layouts) { %> <% if (features.layouts) { %>
@ -146,9 +156,13 @@ export default async (ssrContext) => {
** Set layout ** Set layout
*/ */
let layout = Components.length ? Components[0].options.layout : NuxtError.layout let layout = Components.length ? Components[0].options.layout : NuxtError.layout
if (typeof layout === 'function') layout = layout(app.context) if (typeof layout === 'function') {
layout = layout(app.context)
}
await _app.loadLayout(layout) await _app.loadLayout(layout)
if (ssrContext.nuxt.error) return renderErrorPage() if (ssrContext.nuxt.error) {
return renderErrorPage()
}
layout = _app.setLayout(layout) layout = _app.setLayout(layout)
ssrContext.nuxt.layout = _app.layoutName ssrContext.nuxt.layout = _app.layoutName
<% } %> <% } %>
@ -170,7 +184,9 @@ export default async (ssrContext) => {
} }
}) })
midd = midd.map((name) => { midd = midd.map((name) => {
if (typeof name === 'function') return name if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') { if (typeof middleware[name] !== 'function') {
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name }) app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
} }
@ -178,8 +194,12 @@ export default async (ssrContext) => {
}) })
await middlewareSeries(midd, app.context) await middlewareSeries(midd, app.context)
// ...If there is a redirect or an error, stop the process // ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) return noopApp() if (ssrContext.redirected) {
if (ssrContext.nuxt.error) return renderErrorPage() return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
<% } %> <% } %>
<% if (features.validate) { %> <% if (features.validate) { %>
@ -211,14 +231,18 @@ export default async (ssrContext) => {
// ...If .validate() returned false // ...If .validate() returned false
if (!isValid) { if (!isValid) {
// Don't server-render the page in generate mode // Don't server-render the page in generate mode
if (ssrContext._generate) ssrContext.nuxt.serverRendered = false if (ssrContext._generate) {
ssrContext.nuxt.serverRendered = false
}
// Render a 404 error page // Render a 404 error page
return render404Page() return render404Page()
} }
<% } %> <% } %>
// If no Components found, returns 404 // If no Components found, returns 404
if (!Components.length) return render404Page() if (!Components.length) {
return render404Page()
}
<% if (features.asyncData || features.fetch) { %> <% if (features.asyncData || features.fetch) { %>
// Call asyncData & fetch hooks on components matched by the route. // Call asyncData & fetch hooks on components matched by the route.
@ -252,15 +276,19 @@ export default async (ssrContext) => {
return Promise.all(promises) return Promise.all(promises)
})) }))
<% if (debug) { %>if (process.env.DEBUG && asyncDatas.length)console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')<% } %> <% if (debug) { %>if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')<% } %>
// datas are the first row of each // datas are the first row of each
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {}) ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
<% } %> <% } %>
// ...If there is a redirect or an error, stop the process // ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) return noopApp() if (ssrContext.redirected) {
if (ssrContext.nuxt.error) return renderErrorPage() return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
// Call beforeNuxtRender methods & add store state // Call beforeNuxtRender methods & add store state
await beforeRender() await beforeRender()

View File

@ -3,10 +3,14 @@ import Vuex from 'vuex'
Vue.use(Vuex) Vue.use(Vuex)
<%
const willResolveStoreModules = storeModules.some(s => s.src.indexOf('index.') !== 0)
if (willResolveStoreModules) { %>
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations'] const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
<% } %>
let store = {} let store = {}
void (function updateModules() { void (function updateModules () {
<% storeModules.some(s => { <% storeModules.some(s => {
if(s.src.indexOf('index.') === 0) { %> if(s.src.indexOf('index.') === 0) { %>
store = normalizeRoot(require('<%= relativeToBuild(srcDir, dir.store, s.src) %>'), '<%= dir.store %>/<%= s.src %>') store = normalizeRoot(require('<%= relativeToBuild(srcDir, dir.store, s.src) %>'), '<%= dir.store %>/<%= s.src %>')
@ -15,6 +19,7 @@ void (function updateModules() {
// If store is an exported method = classic mode (deprecated) // If store is an exported method = classic mode (deprecated)
<% if (isDev) { %> <% if (isDev) { %>
if (typeof store === 'function') { if (typeof store === 'function') {
<%= isTest ? '// eslint-disable-next-line no-console' : '' %>
return console.warn('Classic mode for store/ is deprecated and will be removed in Nuxt 3.') return console.warn('Classic mode for store/ is deprecated and will be removed in Nuxt 3.')
}<% } %> }<% } %>
@ -47,7 +52,34 @@ export const createStore = store instanceof Function ? store : () => {
}, store)) }, store))
} }
function resolveStoreModules(moduleData, filename) { function normalizeRoot (moduleData, filePath) {
moduleData = moduleData.default || moduleData
if (moduleData.commit) {
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
}
if (typeof moduleData !== 'function') {
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData)
}
return normalizeModule(moduleData, filePath)
}
function normalizeModule (moduleData, filePath) {
if (moduleData.state && typeof moduleData.state !== 'function') {
<%= isTest ? '// eslint-disable-next-line no-console' : '' %>
console.warn(`'state' should be a method that returns an object in ${filePath}`)
const state = Object.assign({}, moduleData.state)
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData, { state: () => state })
}
return moduleData
}
<% if (willResolveStoreModules) { %>
function resolveStoreModules (moduleData, filename) {
moduleData = moduleData.default || moduleData moduleData = moduleData.default || moduleData
// Remove store src + extension (./foo/index.js -> foo/index) // Remove store src + extension (./foo/index.js -> foo/index)
const namespace = filename.replace(/\.(<%= extensions %>)$/, '') const namespace = filename.replace(/\.(<%= extensions %>)$/, '')
@ -87,22 +119,9 @@ function resolveStoreModules(moduleData, filename) {
} }
} }
function normalizeRoot(moduleData, filePath) { function normalizeState (moduleData, filePath) {
moduleData = moduleData.default || moduleData
if (moduleData.commit) {
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
}
if (typeof moduleData !== 'function') {
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData)
}
return normalizeModule(moduleData, filePath)
}
function normalizeState(moduleData, filePath) {
if (typeof moduleData !== 'function') { if (typeof moduleData !== 'function') {
<%= isTest ? '// eslint-disable-next-line no-console' : '' %>
console.warn(`${filePath} should export a method that returns an object`) console.warn(`${filePath} should export a method that returns an object`)
const state = Object.assign({}, moduleData) const state = Object.assign({}, moduleData)
return () => state return () => state
@ -110,17 +129,7 @@ function normalizeState(moduleData, filePath) {
return normalizeModule(moduleData, filePath) return normalizeModule(moduleData, filePath)
} }
function normalizeModule(moduleData, filePath) { function getStoreModule (storeModule, namespaces, { isProperty = false } = {}) {
if (moduleData.state && typeof moduleData.state !== 'function') {
console.warn(`'state' should be a method that returns an object in ${filePath}`)
const state = Object.assign({}, moduleData.state)
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData, { state: () => state })
}
return moduleData
}
function getStoreModule(storeModule, namespaces, { isProperty = false } = {}) {
// If ./mutations.js // If ./mutations.js
if (!namespaces.length || (isProperty && namespaces.length === 1)) { if (!namespaces.length || (isProperty && namespaces.length === 1)) {
return storeModule return storeModule
@ -135,8 +144,10 @@ function getStoreModule(storeModule, namespaces, { isProperty = false } = {}) {
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty }) return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
} }
function mergeProperty(storeModule, moduleData, property) { function mergeProperty (storeModule, moduleData, property) {
if (!moduleData) return if (!moduleData) {
return
}
if (property === 'state') { if (property === 'state') {
storeModule.state = moduleData || storeModule.state storeModule.state = moduleData || storeModule.state
@ -144,3 +155,4 @@ function mergeProperty(storeModule, moduleData, property) {
storeModule[property] = Object.assign({}, storeModule[property], moduleData) storeModule[property] = Object.assign({}, storeModule[property], moduleData)
} }
} }
<% } %>

View File

@ -9,20 +9,20 @@ if (process.client) {
} }
} }
export function empty() {} export function empty () {}
export function globalHandleError(error) { export function globalHandleError (error) {
if (Vue.config.errorHandler) { if (Vue.config.errorHandler) {
Vue.config.errorHandler(error) Vue.config.errorHandler(error)
} }
} }
export function interopDefault(promise) { export function interopDefault (promise) {
return promise.then(m => m.default || m) return promise.then(m => m.default || m)
} }
<% if (features.asyncData) { %> <% if (features.asyncData) { %>
export function applyAsyncData(Component, asyncData) { export function applyAsyncData (Component, asyncData) {
if ( if (
// For SSR, we once all this function without second param to just apply asyncData // For SSR, we once all this function without second param to just apply asyncData
// Prevent doing this for each SSR request // Prevent doing this for each SSR request
@ -50,7 +50,7 @@ export function applyAsyncData(Component, asyncData) {
} }
<% } %> <% } %>
export function sanitizeComponent(Component) { export function sanitizeComponent (Component) {
// If Component already sanitized // If Component already sanitized
if (Component.options && Component._Ctor === Component) { if (Component.options && Component._Ctor === Component) {
return Component return Component
@ -69,7 +69,7 @@ export function sanitizeComponent(Component) {
return Component return Component
} }
export function getMatchedComponents(route, matches = false, prop = 'components') { export function getMatchedComponents (route, matches = false, prop = 'components') {
return Array.prototype.concat.apply([], route.matched.map((m, index) => { return Array.prototype.concat.apply([], route.matched.map((m, index) => {
return Object.keys(m[prop]).map((key) => { return Object.keys(m[prop]).map((key) => {
matches && matches.push(index) matches && matches.push(index)
@ -78,11 +78,11 @@ export function getMatchedComponents(route, matches = false, prop = 'components'
})) }))
} }
export function getMatchedComponentsInstances(route, matches = false) { export function getMatchedComponentsInstances (route, matches = false) {
return getMatchedComponents(route, matches, 'instances') return getMatchedComponents(route, matches, 'instances')
} }
export function flatMapComponents(route, fn) { export function flatMapComponents (route, fn) {
return Array.prototype.concat.apply([], route.matched.map((m, index) => { return Array.prototype.concat.apply([], route.matched.map((m, index) => {
return Object.keys(m.components).reduce((promises, key) => { return Object.keys(m.components).reduce((promises, key) => {
if (m.components[key]) { if (m.components[key]) {
@ -95,7 +95,7 @@ export function flatMapComponents(route, fn) {
})) }))
} }
export function resolveRouteComponents(route, fn) { export function resolveRouteComponents (route, fn) {
return Promise.all( return Promise.all(
flatMapComponents(route, async (Component, instance, match, key) => { flatMapComponents(route, async (Component, instance, match, key) => {
// If component is a function, resolve it // If component is a function, resolve it
@ -108,7 +108,7 @@ export function resolveRouteComponents(route, fn) {
) )
} }
export async function getRouteData(route) { export async function getRouteData (route) {
if (!route) { if (!route) {
return return
} }
@ -123,7 +123,7 @@ export async function getRouteData(route) {
} }
} }
export async function setContext(app, context) { export async function setContext (app, context) {
// If context not defined, create it // If context not defined, create it
if (!app.context) { if (!app.context) {
app.context = { app.context = {
@ -217,7 +217,7 @@ export async function setContext(app, context) {
app.context.query = app.context.route.query || {} app.context.query = app.context.route.query || {}
} }
<% if (features.middleware) { %> <% if (features.middleware) { %>
export function middlewareSeries(promises, appContext) { export function middlewareSeries (promises, appContext) {
if (!promises.length || appContext._redirected || appContext._errored) { if (!promises.length || appContext._redirected || appContext._errored) {
return Promise.resolve() return Promise.resolve()
} }
@ -227,7 +227,7 @@ export function middlewareSeries(promises, appContext) {
}) })
} }
<% } %> <% } %>
export function promisify(fn, context) { export function promisify (fn, context) {
<% if (features.deprecations) { %> <% if (features.deprecations) { %>
let promise let promise
if (fn.length === 2) { if (fn.length === 2) {
@ -250,7 +250,7 @@ export function promisify(fn, context) {
promise = fn(context) promise = fn(context)
} }
<% } else { %> <% } else { %>
const promise = fn(context) const promise = fn(context)
<% } %> <% } %>
if (promise && promise instanceof Promise && typeof promise.then === 'function') { if (promise && promise instanceof Promise && typeof promise.then === 'function') {
return promise return promise
@ -259,7 +259,7 @@ export function promisify(fn, context) {
} }
// Imported from vue-router // Imported from vue-router
export function getLocation(base, mode) { export function getLocation (base, mode) {
let path = decodeURI(window.location.pathname) let path = decodeURI(window.location.pathname)
if (mode === 'hash') { if (mode === 'hash') {
return window.location.hash.replace(/^#\//, '') return window.location.hash.replace(/^#\//, '')
@ -279,11 +279,11 @@ export function getLocation(base, mode) {
* @param {Object=} options * @param {Object=} options
* @return {!function(Object=, Object=)} * @return {!function(Object=, Object=)}
*/ */
export function compile(str, options) { export function compile (str, options) {
return tokensToFunction(parse(str, options)) return tokensToFunction(parse(str, options))
} }
export function getQueryDiff(toQuery, fromQuery) { export function getQueryDiff (toQuery, fromQuery) {
const diff = {} const diff = {}
const queries = { ...toQuery, ...fromQuery } const queries = { ...toQuery, ...fromQuery }
for (const k in queries) { for (const k in queries) {
@ -294,7 +294,7 @@ export function getQueryDiff(toQuery, fromQuery) {
return diff return diff
} }
export function normalizeError(err) { export function normalizeError (err) {
let message let message
if (!(err.message || typeof err === 'string')) { if (!(err.message || typeof err === 'string')) {
try { try {
@ -337,7 +337,7 @@ const PATH_REGEXP = new RegExp([
* @param {Object=} options * @param {Object=} options
* @return {!Array} * @return {!Array}
*/ */
function parse(str, options) { function parse (str, options) {
const tokens = [] const tokens = []
let key = 0 let key = 0
let index = 0 let index = 0
@ -409,7 +409,7 @@ function parse(str, options) {
* @param {string} * @param {string}
* @return {string} * @return {string}
*/ */
function encodeURIComponentPretty(str, slashAllowed) { function encodeURIComponentPretty (str, slashAllowed) {
const re = slashAllowed ? /[?#]/g : /[/?#]/g const re = slashAllowed ? /[?#]/g : /[/?#]/g
return encodeURI(str).replace(re, (c) => { return encodeURI(str).replace(re, (c) => {
return '%' + c.charCodeAt(0).toString(16).toUpperCase() return '%' + c.charCodeAt(0).toString(16).toUpperCase()
@ -422,7 +422,7 @@ function encodeURIComponentPretty(str, slashAllowed) {
* @param {string} * @param {string}
* @return {string} * @return {string}
*/ */
function encodeAsterisk(str) { function encodeAsterisk (str) {
return encodeURIComponentPretty(str, true) return encodeURIComponentPretty(str, true)
} }
@ -432,7 +432,7 @@ function encodeAsterisk(str) {
* @param {string} str * @param {string} str
* @return {string} * @return {string}
*/ */
function escapeString(str) { function escapeString (str) {
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1') return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
} }
@ -442,14 +442,14 @@ function escapeString(str) {
* @param {string} group * @param {string} group
* @return {string} * @return {string}
*/ */
function escapeGroup(group) { function escapeGroup (group) {
return group.replace(/([=!:$/()])/g, '\\$1') return group.replace(/([=!:$/()])/g, '\\$1')
} }
/** /**
* Expose a method for transforming tokens into the path function. * Expose a method for transforming tokens into the path function.
*/ */
function tokensToFunction(tokens) { function tokensToFunction (tokens) {
// Compile all the tokens into regexps. // Compile all the tokens into regexps.
const matches = new Array(tokens.length) const matches = new Array(tokens.length)
@ -537,7 +537,7 @@ function tokensToFunction(tokens) {
* @param {string} query * @param {string} query
* @return {string} * @return {string}
*/ */
function formatUrl(url, query) { function formatUrl (url, query) {
<% if (features.clientUseUrl) { %> <% if (features.clientUseUrl) { %>
url = new URL(url, top.location.href) url = new URL(url, top.location.href)
for (const key in query) { for (const key in query) {
@ -592,7 +592,7 @@ function formatUrl(url, query) {
* @param {object} query * @param {object} query
* @return {string} * @return {string}
*/ */
function formatQuery(query) { function formatQuery (query) {
return Object.keys(query).sort().map((key) => { return Object.keys(query).sort().map((key) => {
const val = query[key] const val = query[key]
if (val == null) { if (val == null) {

View File

@ -98,7 +98,9 @@
<script> <script>
window.addEventListener('error', function () { window.addEventListener('error', function () {
var e = document.getElementById('nuxt-loading'); var e = document.getElementById('nuxt-loading');
if (e) e.className += ' error'; if (e) {
e.className += ' error';
}
}); });
</script> </script>

View File

@ -21,7 +21,7 @@ describe('nuxt minimal vue-app bundle size limit', () => {
const filter = filename => filename === 'vue-app.nuxt.js' const filter = filename => filename === 'vue-app.nuxt.js'
const legacyResourcesSize = await getResourcesSize(distDir, 'client', { filter }) const legacyResourcesSize = await getResourcesSize(distDir, 'client', { filter })
const LEGACY_JS_RESOURCES_KB_SIZE = 15.8 const LEGACY_JS_RESOURCES_KB_SIZE = 15.2
expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE) expect(legacyResourcesSize.uncompressed).toBeWithinSize(LEGACY_JS_RESOURCES_KB_SIZE)
}) })
}) })

View File

@ -4615,10 +4615,10 @@ eslint-module-utils@^2.4.0:
debug "^2.6.8" debug "^2.6.8"
pkg-dir "^2.0.0" pkg-dir "^2.0.0"
eslint-multiplexer@^1.0.4: eslint-multiplexer@^2.0.0:
version "1.0.4" version "2.0.0"
resolved "https://registry.npmjs.org/eslint-multiplexer/-/eslint-multiplexer-1.0.4.tgz#4dde202fa2c9edc2ee802ab4b8fdb02a49b6d596" resolved "https://registry.npmjs.org/eslint-multiplexer/-/eslint-multiplexer-2.0.0.tgz#34d00218f5027b417b89a4add81f1536d4197a7d"
integrity sha512-KdakcevBFb6n4VQBRY7u+wFqyREzz1QYJRuuPNHUcK5hN1XOikJIEC5Qf+28ZRIvHkoxiw/CW//LNAAyQzxaiQ== integrity sha512-hZA86lQ6d3srMwVlEjYVsX24mk/AImm4zL0r0AR8jNsJYoznQT7O2xgeaT6gNsLIxzRP79VkpeMVRxu4+GRWOA==
dependencies: dependencies:
cross-spawn "^6.0.5" cross-spawn "^6.0.5"
minimist "^1.2.0" minimist "^1.2.0"