1 | import Vue from 'vue'
|
2 | <% if (fetch.client) { %>import fetch from 'unfetch'<% } %>
|
3 | <% if (features.middleware) { %>import middleware from './middleware.js'<% } %>
|
4 | import {
|
5 | <% if (features.asyncData) { %>applyAsyncData,
|
6 | promisify,<% } %>
|
7 | <% if (features.middleware) { %>middlewareSeries,<% } %>
|
8 | <% if (features.transitions || (features.middleware && features.layouts)) { %>sanitizeComponent,<% } %>
|
9 | resolveRouteComponents,
|
10 | getMatchedComponents,
|
11 | getMatchedComponentsInstances,
|
12 | flatMapComponents,
|
13 | setContext,
|
14 | <% if (features.transitions || features.asyncData || features.fetch) { %>getLocation,<% } %>
|
15 | compile,
|
16 | getQueryDiff,
|
17 | globalHandleError,
|
18 | isSamePath
|
19 | } from './utils.js'
|
20 | import { createApp<% if (features.layouts) { %>, NuxtError<% } %> } from './index.js'
|
21 | <% if (features.fetch) { %>import fetchMixin from './mixins/fetch.client'<% } %>
|
22 | import NuxtLink from './components/nuxt-link.<%= features.clientPrefetch ? "client" : "server" %>.js'
|
23 | <% if (isFullStatic) { %>import './jsonp'<% } %>
|
24 |
|
25 | <% if (features.fetch) { %>
|
26 |
|
27 | if (!Vue.__nuxt__fetch__mixin__) {
|
28 | Vue.mixin(fetchMixin)
|
29 | Vue.__nuxt__fetch__mixin__ = true
|
30 | }
|
31 | <% } %>
|
32 |
|
33 |
|
34 | Vue.component(NuxtLink.name, NuxtLink)
|
35 | <% if (features.componentAliases) { %>Vue.component('NLink', NuxtLink)<% } %>
|
36 |
|
37 | <% if (fetch.client) { %>if (!global.fetch) { global.fetch = fetch }<% } %>
|
38 |
|
39 |
|
40 | let _lastPaths = []<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %>
|
41 | let app
|
42 | let router
|
43 | <% if (store) { %>let store<%= isTest ? '// eslint-disable-line no-unused-vars' : '' %><% } %>
|
44 |
|
45 | // Try to rehydrate SSR data from window
|
46 | const NUXT = window.<%= globals.context %> || {}
|
47 |
|
48 | Object.assign(Vue.config, <%= serialize(vue.config) %>)<%= isTest ? '// eslint-disable-line' : '' %>
|
49 |
|
50 | <% if (nuxtOptions.render.ssrLog) { %>
|
51 | const logs = NUXT.logs || []
|
52 | if (logs.length > 0) {
|
53 | const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
|
54 | console.group && console.group<%= nuxtOptions.render.ssrLog === 'collapsed' ? 'Collapsed' : '' %> ('%cNuxt SSR', ssrLogStyle)
|
55 | logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args))
|
56 | delete NUXT.logs
|
57 | console.groupEnd && console.groupEnd()
|
58 | }
|
59 | <% } %>
|
60 | <% if (debug) { %>
|
61 | // Setup global Vue error handler
|
62 | if (!Vue.config.$nuxt) {
|
63 | const defaultErrorHandler = Vue.config.errorHandler
|
64 | Vue.config.errorHandler = async (err, vm, info, ...rest) => {
|
65 | // Call other handler if exist
|
66 | let handled = null
|
67 | if (typeof defaultErrorHandler === 'function') {
|
68 | handled = defaultErrorHandler(err, vm, info, ...rest)
|
69 | }
|
70 | if (handled === true) {
|
71 | return handled
|
72 | }
|
73 |
|
74 | if (vm && vm.$root) {
|
75 | const nuxtApp = Object.keys(Vue.config.$nuxt)
|
76 | .find(nuxtInstance => vm.$root[nuxtInstance])
|
77 |
|
78 | // Show Nuxt Error Page
|
79 | if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
|
80 | const currentApp = vm.$root[nuxtApp]
|
81 | <% if (features.layouts) { %>
|
82 | // Load error layout
|
83 | let layout = (NuxtError.options || NuxtError).layout
|
84 | if (typeof layout === 'function') {
|
85 | layout = layout(currentApp.context)
|
86 | }
|
87 | if (layout) {
|
88 | await currentApp.loadLayout(layout).catch(() => {})
|
89 | }
|
90 | currentApp.setLayout(layout)
|
91 | <% } %>
|
92 | currentApp.error(err)
|
93 | }
|
94 | }
|
95 |
|
96 | if (typeof defaultErrorHandler === 'function') {
|
97 | return handled
|
98 | }
|
99 |
|
100 | // Log to console
|
101 | if (process.env.NODE_ENV !== 'production') {
|
102 | console.error(err)
|
103 | } else {
|
104 | console.error(err.message || err)
|
105 | }
|
106 | }
|
107 | Vue.config.$nuxt = {}
|
108 | }
|
109 | Vue.config.$nuxt.<%= globals.nuxt %> = true
|
110 | <% } %>
|
111 | const errorHandler = Vue.config.errorHandler || console.error
|
112 |
|
113 | // Create and mount App
|
114 | createApp(null, NUXT.config).then(mountApp).catch(errorHandler)
|
115 |
|
116 | <% if (features.transitions) { %>
|
117 | function componentOption (component, key, ...args) {
|
118 | if (!component || !component.options || !component.options[key]) {
|
119 | return {}
|
120 | }
|
121 | const option = component.options[key]
|
122 | if (typeof option === 'function') {
|
123 | return option(...args)
|
124 | }
|
125 | return option
|
126 | }
|
127 |
|
128 | function mapTransitions (toComponents, to, from) {
|
129 | const componentTransitions = (component) => {
|
130 | const transition = componentOption(component, 'transition', to, from) || {}
|
131 | return (typeof transition === 'string' ? { name: transition } : transition)
|
132 | }
|
133 |
|
134 | const fromComponents = from ? getMatchedComponents(from) : []
|
135 | const maxDepth = Math.max(toComponents.length, fromComponents.length)
|
136 |
|
137 | const mergedTransitions = []
|
138 | for (let i=0; i<maxDepth; i++) {
|
139 | // Clone original objects to prevent overrides
|
140 | const toTransitions = Object.assign({}, componentTransitions(toComponents[i]))
|
141 | const transitions = Object.assign({}, componentTransitions(fromComponents[i]))
|
142 |
|
143 | // Combine transitions & prefer `leave` properties of "from" route
|
144 | Object.keys(toTransitions)
|
145 | .filter(key => typeof toTransitions[key] !== 'undefined' && !key.toLowerCase().includes('leave'))
|
146 | .forEach((key) => { transitions[key] = toTransitions[key] })
|
147 |
|
148 | mergedTransitions.push(transitions)
|
149 | }
|
150 | return mergedTransitions
|
151 | }
|
152 | <% } %>
|
153 | async function loadAsyncComponents (to, from, next) {
|
154 | // Check if route changed (this._routeChanged), only if the page is not an error (for validate())
|
155 | this._routeChanged = Boolean(app.nuxt.err) || from.name !== to.name
|
156 | this._paramChanged = !this._routeChanged && from.path !== to.path
|
157 | this._queryChanged = !this._paramChanged && from.fullPath !== to.fullPath
|
158 | this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : [])
|
159 |
|
160 | <% if (loading) { %>
|
161 | if ((this._routeChanged || this._paramChanged) && this.$loading.start && !this.$loading.manual) {
|
162 | this.$loading.start()
|
163 | }
|
164 | <% } %>
|
165 |
|
166 | try {
|
167 | if (this._queryChanged) {
|
168 | const Components = await resolveRouteComponents(
|
169 | to,
|
170 | (Component, instance) => ({ Component, instance })
|
171 | )
|
172 | // Add a marker on each component that it needs to refresh or not
|
173 | const startLoader = Components.some(({ Component, instance }) => {
|
174 | const watchQuery = Component.options.watchQuery
|
175 | if (watchQuery === true) {
|
176 | return true
|
177 | }
|
178 | if (Array.isArray(watchQuery)) {
|
179 | return watchQuery.some(key => this._diffQuery[key])
|
180 | }
|
181 | if (typeof watchQuery === 'function') {
|
182 | return watchQuery.apply(instance, [to.query, from.query])
|
183 | }
|
184 | return false
|
185 | })
|
186 | <% if (loading) { %>
|
187 | if (startLoader && this.$loading.start && !this.$loading.manual) {
|
188 | this.$loading.start()
|
189 | }
|
190 | <% } %>
|
191 | }
|
192 | // Call next()
|
193 | next()
|
194 | } catch (error) {
|
195 | const err = error || {}
|
196 | const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
|
197 | const message = err.message || ''
|
198 |
|
199 | // Handle chunk loading errors
|
200 | // This may be due to a new deployment or a network problem
|
201 | if (/^Loading( CSS)? chunk (\d)+ failed\./.test(message)) {
|
202 | window.location.reload(true /* skip cache */)
|
203 | return // prevent error page blinking for user
|
204 | }
|
205 |
|
206 | this.error({ statusCode, message })
|
207 | this.<%= globals.nuxt %>.$emit('routeChanged', to, from, err)
|
208 | next()
|
209 | }
|
210 | }
|
211 |
|
212 | <% if (features.transitions || features.asyncData || features.fetch) { %>
|
213 | function applySSRData (Component, ssrData) {
|
214 | <% if (features.asyncData) { %>
|
215 | if (NUXT.serverRendered && ssrData) {
|
216 | applyAsyncData(Component, ssrData)
|
217 | }
|
218 | <% } %>
|
219 | Component._Ctor = Component
|
220 | return Component
|
221 | }
|
222 |
|
223 | // Get matched components
|
224 | function resolveComponents (router) {
|
225 | const path = getLocation(router.options.base, router.options.mode)
|
226 |
|
227 | return flatMapComponents(router.match(path), async (Component, _, match, key, index) => {
|
228 | // If component is not resolved yet, resolve it
|
229 | if (typeof Component === 'function' && !Component.options) {
|
230 | Component = await Component()
|
231 | }
|
232 | // Sanitize it and save it
|
233 | const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null)
|
234 | match.components[key] = _Component
|
235 | return _Component
|
236 | })
|
237 | }
|
238 | <% } %>
|
239 |
|
240 | <% if (features.middleware) { %>
|
241 | function callMiddleware (Components, context, layout) {
|
242 | let midd = <%= devalue(router.middleware) %><%= isTest ? '// eslint-disable-line' : '' %>
|
243 | let unknownMiddleware = false
|
244 |
|
245 | <% if (features.layouts) { %>
|
246 | // If layout is undefined, only call global middleware
|
247 | if (typeof layout !== 'undefined') {
|
248 | midd = [] // Exclude global middleware if layout defined (already called before)
|
249 | layout = sanitizeComponent(layout)
|
250 | if (layout.options.middleware) {
|
251 | midd = midd.concat(layout.options.middleware)
|
252 | }
|
253 | Components.forEach((Component) => {
|
254 | if (Component.options.middleware) {
|
255 | midd = midd.concat(Component.options.middleware)
|
256 | }
|
257 | })
|
258 | }
|
259 | <% } %>
|
260 |
|
261 | midd = midd.map((name) => {
|
262 | if (typeof name === 'function') {
|
263 | return name
|
264 | }
|
265 | if (typeof middleware[name] !== 'function') {
|
266 | unknownMiddleware = true
|
267 | this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
268 | }
|
269 | return middleware[name]
|
270 | })
|
271 |
|
272 | if (unknownMiddleware) {
|
273 | return
|
274 | }
|
275 | return middlewareSeries(midd, context)
|
276 | }
|
277 | <% } else if (isDev) {
|
278 | // This is a placeholder function mainly so we dont have to
|
279 | // refactor the promise chain in addHotReload()
|
280 | %>
|
281 | function callMiddleware () {
|
282 | return Promise.resolve(true)
|
283 | }
|
284 | <% } %>
|
285 | async function render (to, from, next) {
|
286 | if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
|
287 | return next()
|
288 | }
|
289 | // Handle first render on SPA mode
|
290 | let spaFallback = false
|
291 | if (to === from) {
|
292 | _lastPaths = []
|
293 | spaFallback = true
|
294 | } else {
|
295 | const fromMatches = []
|
296 | _lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => {
|
297 | return compile(from.matched[fromMatches[i]].path)(from.params)
|
298 | })
|
299 | }
|
300 |
|
301 | // nextCalled is true when redirected
|
302 | let nextCalled = false
|
303 | const _next = (path) => {
|
304 | <% if (loading) { %>
|
305 | if (from.path === path.path && this.$loading.finish) {
|
306 | this.$loading.finish()
|
307 | }
|
308 | <% } %>
|
309 | <% if (loading) { %>
|
310 | if (from.path !== path.path && this.$loading.pause) {
|
311 | this.$loading.pause()
|
312 | }
|
313 | <% } %>
|
314 | if (nextCalled) {
|
315 | return
|
316 | }
|
317 |
|
318 | nextCalled = true
|
319 | next(path)
|
320 | }
|
321 |
|
322 | // Update context
|
323 | await setContext(app, {
|
324 | route: to,
|
325 | from,
|
326 | next: _next.bind(this)
|
327 | })
|
328 | this._dateLastError = app.nuxt.dateErr
|
329 | this._hadError = Boolean(app.nuxt.err)
|
330 |
|
331 | // Get route's matched components
|
332 | const matches = []
|
333 | const Components = getMatchedComponents(to, matches)
|
334 |
|
335 | // If no Components matched, generate 404
|
336 | if (!Components.length) {
|
337 | <% if (features.middleware) { %>
|
338 | // Default layout
|
339 | await callMiddleware.call(this, Components, app.context)
|
340 | if (nextCalled) {
|
341 | return
|
342 | }
|
343 | <% } %>
|
344 |
|
345 | <% if (features.layouts) { %>
|
346 | // Load layout for error page
|
347 | const errorLayout = (NuxtError.options || NuxtError).layout
|
348 | const layout = await this.loadLayout(
|
349 | typeof errorLayout === 'function'
|
350 | ? errorLayout.call(NuxtError, app.context)
|
351 | : errorLayout
|
352 | )
|
353 | <% } %>
|
354 |
|
355 | <% if (features.middleware) { %>
|
356 | await callMiddleware.call(this, Components, app.context, layout)
|
357 | if (nextCalled) {
|
358 | return
|
359 | }
|
360 | <% } %>
|
361 |
|
362 | // Show error page
|
363 | app.context.error({ statusCode: 404, message: '<%= messages.error_404 %>' })
|
364 | return next()
|
365 | }
|
366 |
|
367 | <% if (features.asyncData || features.fetch) { %>
|
368 | // Update ._data and other properties if hot reloaded
|
369 | Components.forEach((Component) => {
|
370 | if (Component._Ctor && Component._Ctor.options) {
|
371 | <% if (features.asyncData) { %>Component.options.asyncData = Component._Ctor.options.asyncData<% } %>
|
372 | <% if (features.fetch) { %>Component.options.fetch = Component._Ctor.options.fetch<% } %>
|
373 | }
|
374 | })
|
375 | <% } %>
|
376 |
|
377 | <% if (features.transitions) { %>
|
378 | // Apply transitions
|
379 | this.setTransitions(mapTransitions(Components, to, from))
|
380 | <% } %>
|
381 | try {
|
382 | <% if (features.middleware) { %>
|
383 | // Call middleware
|
384 | await callMiddleware.call(this, Components, app.context)
|
385 | if (nextCalled) {
|
386 | return
|
387 | }
|
388 | if (app.context._errored) {
|
389 | return next()
|
390 | }
|
391 | <% } %>
|
392 |
|
393 | <% if (features.layouts) { %>
|
394 | // Set layout
|
395 | let layout = Components[0].options.layout
|
396 | if (typeof layout === 'function') {
|
397 | layout = layout(app.context)
|
398 | }
|
399 | layout = await this.loadLayout(layout)
|
400 | <% } %>
|
401 |
|
402 | <% if (features.middleware) { %>
|
403 | // Call middleware for layout
|
404 | await callMiddleware.call(this, Components, app.context, layout)
|
405 | if (nextCalled) {
|
406 | return
|
407 | }
|
408 | if (app.context._errored) {
|
409 | return next()
|
410 | }
|
411 | <% } %>
|
412 |
|
413 |
|
414 | <% if (features.validate) { %>
|
415 | // Call .validate()
|
416 | let isValid = true
|
417 | try {
|
418 | for (const Component of Components) {
|
419 | if (typeof Component.options.validate !== 'function') {
|
420 | continue
|
421 | }
|
422 |
|
423 | isValid = await Component.options.validate(app.context)
|
424 |
|
425 | if (!isValid) {
|
426 | break
|
427 | }
|
428 | }
|
429 | } catch (validationError) {
|
430 | // ...If .validate() threw an error
|
431 | this.error({
|
432 | statusCode: validationError.statusCode || '500',
|
433 | message: validationError.message
|
434 | })
|
435 | return next()
|
436 | }
|
437 |
|
438 | // ...If .validate() returned false
|
439 | if (!isValid) {
|
440 | this.error({ statusCode: 404, message: '<%= messages.error_404 %>' })
|
441 | return next()
|
442 | }
|
443 | <% } %>
|
444 |
|
445 | <% if (features.asyncData || features.fetch) { %>
|
446 | let instances
|
447 | // Call asyncData & fetch hooks on components matched by the route.
|
448 | await Promise.all(Components.map(async (Component, i) => {
|
449 | // Check if only children route changed
|
450 | Component._path = compile(to.matched[matches[i]].path)(to.params)
|
451 | Component._dataRefresh = false
|
452 | const childPathChanged = Component._path !== _lastPaths[i]
|
453 | // Refresh component (call asyncData & fetch) when:
|
454 | // Route path changed part includes current component
|
455 | // Or route param changed part includes current component and watchParam is not `false`
|
456 | // Or route query is changed and watchQuery returns `true`
|
457 | if (this._routeChanged && childPathChanged) {
|
458 | Component._dataRefresh = true
|
459 | } else if (this._paramChanged && childPathChanged) {
|
460 | const watchParam = Component.options.watchParam
|
461 | Component._dataRefresh = watchParam !== false
|
462 | } else if (this._queryChanged) {
|
463 | const watchQuery = Component.options.watchQuery
|
464 | if (watchQuery === true) {
|
465 | Component._dataRefresh = true
|
466 | } else if (Array.isArray(watchQuery)) {
|
467 | Component._dataRefresh = watchQuery.some(key => this._diffQuery[key])
|
468 | } else if (typeof watchQuery === 'function') {
|
469 | if (!instances) {
|
470 | instances = getMatchedComponentsInstances(to)
|
471 | }
|
472 | Component._dataRefresh = watchQuery.apply(instances[i], [to.query, from.query])
|
473 | }
|
474 | }
|
475 | if (!this._hadError && this._isMounted && !Component._dataRefresh) {
|
476 | return
|
477 | }
|
478 |
|
479 | const promises = []
|
480 |
|
481 | <% if (features.asyncData) { %>
|
482 | const hasAsyncData = (
|
483 | Component.options.asyncData &&
|
484 | typeof Component.options.asyncData === 'function'
|
485 | )
|
486 | <% } else { %>
|
487 | const hasAsyncData = false
|
488 | <% } %>
|
489 |
|
490 | <% if (features.fetch) { %>
|
491 | const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length
|
492 | <% } else { %>
|
493 | const hasFetch = false
|
494 | <% } %>
|
495 |
|
496 | <% if (loading) { %>
|
497 | const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
|
498 | <% } %>
|
499 |
|
500 | <% if (features.asyncData) { %>
|
501 | // Call asyncData(context)
|
502 | if (hasAsyncData) {
|
503 | <% if (isFullStatic) { %>
|
504 | let promise
|
505 |
|
506 | if (this.isPreview || spaFallback) {
|
507 | promise = promisify(Component.options.asyncData, app.context)
|
508 | } else {
|
509 | promise = this.fetchPayload(to.path)
|
510 | .then(payload => payload.data[i])
|
511 | .catch(_err => promisify(Component.options.asyncData, app.context)) // Fallback
|
512 | }
|
513 | <% } else { %>
|
514 | const promise = promisify(Component.options.asyncData, app.context)
|
515 | <% } %>
|
516 | promise.then((asyncDataResult) => {
|
517 | applyAsyncData(Component, asyncDataResult)
|
518 | <% if (loading) { %>
|
519 | if (this.$loading.increase) {
|
520 | this.$loading.increase(loadingIncrease)
|
521 | }
|
522 | <% } %>
|
523 | })
|
524 | promises.push(promise)
|
525 | }
|
526 | <% } %>
|
527 |
|
528 | <% if (isFullStatic && store) { %>
|
529 | if (!this.isPreview && !spaFallback) {
|
530 | // Replay store mutations, catching to avoid error page on SPA fallback
|
531 | promises.push(this.fetchPayload(to.path).then(payload => {
|
532 | payload.mutations.forEach(m => { this.$store.commit(m[0], m[1]) })
|
533 | }).catch(err => null))
|
534 | }
|
535 | <% } %>
|
536 |
|
537 | // Check disabled page loading
|
538 | this.$loading.manual = Component.options.loading === false
|
539 |
|
540 | <% if (features.fetch) { %>
|
541 | <% if (isFullStatic) { %>
|
542 | if (!this.isPreview && !spaFallback) {
|
543 | // Catching the error here for letting the SPA fallback and normal fetch behaviour
|
544 | promises.push(this.fetchPayload(to.path).catch(err => null))
|
545 | }
|
546 | <% } %>
|
547 | // Call fetch(context)
|
548 | if (hasFetch) {
|
549 | let p = Component.options.fetch(app.context)
|
550 | if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) {
|
551 | p = Promise.resolve(p)
|
552 | }
|
553 | p.then((fetchResult) => {
|
554 | <% if (loading) { %>
|
555 | if (this.$loading.increase) {
|
556 | this.$loading.increase(loadingIncrease)
|
557 | }
|
558 | <% } %>
|
559 | })
|
560 | promises.push(p)
|
561 | }
|
562 | <% } %>
|
563 |
|
564 | return Promise.all(promises)
|
565 | }))
|
566 | <% } %>
|
567 |
|
568 | // If not redirected
|
569 | if (!nextCalled) {
|
570 | <% if (loading) { %>
|
571 | if (this.$loading.finish && !this.$loading.manual) {
|
572 | this.$loading.finish()
|
573 | }
|
574 | <% } %>
|
575 | next()
|
576 | }
|
577 |
|
578 | } catch (err) {
|
579 | const error = err || {}
|
580 | if (error.message === 'ERR_REDIRECT') {
|
581 | return this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
582 | }
|
583 | _lastPaths = []
|
584 |
|
585 | globalHandleError(error)
|
586 |
|
587 | <% if (features.layouts) { %>
|
588 | // Load error layout
|
589 | let layout = (NuxtError.options || NuxtError).layout
|
590 | if (typeof layout === 'function') {
|
591 | layout = layout(app.context)
|
592 | }
|
593 | await this.loadLayout(layout)
|
594 | <% } %>
|
595 |
|
596 | this.error(error)
|
597 | this.<%= globals.nuxt %>.$emit('routeChanged', to, from, error)
|
598 | next()
|
599 | }
|
600 | }
|
601 |
|
602 | // Fix components format in matched, it's due to code-splitting of vue-router
|
603 | function normalizeComponents (to, ___) {
|
604 | flatMapComponents(to, (Component, _, match, key) => {
|
605 | if (typeof Component === 'object' && !Component.options) {
|
606 | // Updated via vue-router resolveAsyncComponents()
|
607 | Component = Vue.extend(Component)
|
608 | Component._Ctor = Component
|
609 | match.components[key] = Component
|
610 | }
|
611 | return Component
|
612 | })
|
613 | }
|
614 |
|
615 | <% if (features.layouts) { %>
|
616 | function setLayoutForNextPage (to) {
|
617 | // Set layout
|
618 | let hasError = Boolean(this.$options.nuxt.err)
|
619 | if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
|
620 | hasError = false
|
621 | }
|
622 | let layout = hasError
|
623 | ? (NuxtError.options || NuxtError).layout
|
624 | : to.matched[0].components.default.options.layout
|
625 |
|
626 | if (typeof layout === 'function') {
|
627 | layout = layout(app.context)
|
628 | }
|
629 | this.setLayout(layout)
|
630 | }
|
631 | <% } %>
|
632 |
|
633 | function checkForErrors (app) {
|
634 | // Hide error component if no error
|
635 | if (app._hadError && app._dateLastError === app.$options.nuxt.dateErr) {
|
636 | app.error()
|
637 | }
|
638 | }
|
639 |
|
640 | // When navigating on a different route but the same component is used, Vue.js
|
641 | // Will not update the instance data, so we have to update $data ourselves
|
642 | function fixPrepatch (to, ___) {
|
643 | if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
|
644 | return
|
645 | }
|
646 |
|
647 | const instances = getMatchedComponentsInstances(to)
|
648 | const Components = getMatchedComponents(to)
|
649 |
|
650 | Vue.nextTick(() => {
|
651 | instances.forEach((instance, i) => {
|
652 | if (!instance || instance._isDestroyed) {
|
653 | return
|
654 | }
|
655 |
|
656 | if (
|
657 | instance.constructor._dataRefresh &&
|
658 | Components[i] === instance.constructor &&
|
659 | instance.$vnode.data.keepAlive !== true &&
|
660 | typeof instance.constructor.options.data === 'function'
|
661 | ) {
|
662 | const newData = instance.constructor.options.data.call(instance)
|
663 | for (const key in newData) {
|
664 | Vue.set(instance.$data, key, newData[key])
|
665 | }
|
666 |
|
667 | // Ensure to trigger scroll event after calling scrollBehavior
|
668 | window.<%= globals.nuxt %>.$nextTick(() => {
|
669 | window.<%= globals.nuxt %>.$emit('triggerScroll')
|
670 | })
|
671 | }
|
672 | })
|
673 | checkForErrors(this)
|
674 | <% if (isDev) { %>
|
675 | // Hot reloading
|
676 | setTimeout(() => hotReloadAPI(this), 100)
|
677 | <% } %>
|
678 | })
|
679 | }
|
680 |
|
681 | function nuxtReady (_app) {
|
682 | window.<%= globals.readyCallback %>Cbs.forEach((cb) => {
|
683 | if (typeof cb === 'function') {
|
684 | cb(_app)
|
685 | }
|
686 | })
|
687 | // Special JSDOM
|
688 | if (typeof window.<%= globals.loadedCallback %> === 'function') {
|
689 | window.<%= globals.loadedCallback %>(_app)
|
690 | }
|
691 | // Add router hooks
|
692 | router.afterEach((to, from) => {
|
693 | // Wait for fixPrepatch + $data updates
|
694 | Vue.nextTick(() => _app.<%= globals.nuxt %>.$emit('routeChanged', to, from))
|
695 | })
|
696 | }
|
697 |
|
698 | <% if (isDev) { %>
|
699 | const noopData = () => { return {} }
|
700 | const noopFetch = () => {}
|
701 |
|
702 | // Special hot reload with asyncData(context)
|
703 | function getNuxtChildComponents ($parent, $components = []) {
|
704 | $parent.$children.forEach(($child) => {
|
705 | if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) {
|
706 | $components.push($child)
|
707 | }
|
708 | if ($child.$children && $child.$children.length) {
|
709 | getNuxtChildComponents($child, $components)
|
710 | }
|
711 | })
|
712 |
|
713 | return $components
|
714 | }
|
715 |
|
716 | function hotReloadAPI(_app) {
|
717 | if (!module.hot) return
|
718 |
|
719 | let $components = getNuxtChildComponents(_app.<%= globals.nuxt %>, [])
|
720 |
|
721 | $components.forEach(addHotReload.bind(_app))
|
722 | }
|
723 |
|
724 | function addHotReload ($component, depth) {
|
725 | if ($component.$vnode.data._hasHotReload) return
|
726 | $component.$vnode.data._hasHotReload = true
|
727 |
|
728 | var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
|
729 |
|
730 | $component.$vnode.context.$forceUpdate = async () => {
|
731 | let Components = getMatchedComponents(router.currentRoute)
|
732 | let Component = Components[depth]
|
733 | if (!Component) {
|
734 | return _forceUpdate()
|
735 | }
|
736 | if (typeof Component === 'object' && !Component.options) {
|
737 | // Updated via vue-router resolveAsyncComponents()
|
738 | Component = Vue.extend(Component)
|
739 | Component._Ctor = Component
|
740 | }
|
741 | this.error()
|
742 | let promises = []
|
743 | const next = function (path) {
|
744 | <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
745 | router.push(path)
|
746 | }
|
747 | await setContext(app, {
|
748 | route: router.currentRoute,
|
749 | isHMR: true,
|
750 | next: next.bind(this)
|
751 | })
|
752 | const context = app.context
|
753 |
|
754 | <% if (loading) { %>
|
755 | if (this.$loading.start && !this.$loading.manual) {
|
756 | this.$loading.start()
|
757 | }
|
758 | <% } %>
|
759 |
|
760 | callMiddleware.call(this, Components, context)
|
761 | .then(() => {
|
762 | <% if (features.layouts) { %>
|
763 | // If layout changed
|
764 | if (depth !== 0) {
|
765 | return
|
766 | }
|
767 |
|
768 | let layout = Component.options.layout || 'default'
|
769 | if (typeof layout === 'function') {
|
770 | layout = layout(context)
|
771 | }
|
772 | if (this.layoutName === layout) {
|
773 | return
|
774 | }
|
775 | let promise = this.loadLayout(layout)
|
776 | promise.then(() => {
|
777 | this.setLayout(layout)
|
778 | Vue.nextTick(() => hotReloadAPI(this))
|
779 | })
|
780 | return promise
|
781 | <% } else { %>
|
782 | return
|
783 | <% } %>
|
784 | })
|
785 | <% if (features.layouts) { %>
|
786 | .then(() => {
|
787 | return callMiddleware.call(this, Components, context, this.layout)
|
788 | })
|
789 | <% } %>
|
790 | .then(() => {
|
791 | <% if (features.asyncData) { %>
|
792 | // Call asyncData(context)
|
793 | let pAsyncData = promisify(Component.options.asyncData || noopData, context)
|
794 | pAsyncData.then((asyncDataResult) => {
|
795 | applyAsyncData(Component, asyncDataResult)
|
796 | <%= (loading ? 'this.$loading.increase && this.$loading.increase(30)' : '') %>
|
797 | })
|
798 | promises.push(pAsyncData)
|
799 | <% } %>
|
800 |
|
801 | <% if (features.fetch) { %>
|
802 | // Call fetch()
|
803 | Component.options.fetch = Component.options.fetch || noopFetch
|
804 | let pFetch = Component.options.fetch.length && Component.options.fetch(context)
|
805 | if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
806 | <%= (loading ? 'pFetch.then(() => this.$loading.increase && this.$loading.increase(30))' : '') %>
|
807 | promises.push(pFetch)
|
808 | <% } %>
|
809 | return Promise.all(promises)
|
810 | })
|
811 | .then(() => {
|
812 | <%= (loading ? 'this.$loading.finish && this.$loading.finish()' : '') %>
|
813 | _forceUpdate()
|
814 | setTimeout(() => hotReloadAPI(this), 100)
|
815 | })
|
816 | }
|
817 | }
|
818 | <% } %>
|
819 |
|
820 | async function mountApp (__app) {
|
821 | // Set global variables
|
822 | app = __app.app
|
823 | router = __app.router
|
824 | <% if (store) { %>store = __app.store<% } %>
|
825 |
|
826 | // Create Vue instance
|
827 | const _app = new Vue(app)
|
828 |
|
829 | <% if (isFullStatic) { %>
|
830 | // Load page chunk
|
831 | if (!NUXT.data && NUXT.serverRendered) {
|
832 | try {
|
833 | const payload = await _app.fetchPayload(NUXT.routePath || _app.context.route.path)
|
834 | Object.assign(NUXT, payload)
|
835 | } catch (err) {}
|
836 | }
|
837 | <% } %>
|
838 |
|
839 | <% if (features.layouts && mode !== 'spa') { %>
|
840 | // Load layout
|
841 | const layout = NUXT.layout || 'default'
|
842 | await _app.loadLayout(layout)
|
843 | _app.setLayout(layout)
|
844 | <% } %>
|
845 |
|
846 | // Mounts Vue app to DOM element
|
847 | const mount = () => {
|
848 | _app.$mount('#<%= globals.id %>')
|
849 |
|
850 | // Add afterEach router hooks
|
851 | router.afterEach(normalizeComponents)
|
852 | <% if (features.layouts) { %>
|
853 | router.afterEach(setLayoutForNextPage.bind(_app))
|
854 | <% } %>
|
855 | router.afterEach(fixPrepatch.bind(_app))
|
856 |
|
857 | // Listen for first Vue update
|
858 | Vue.nextTick(() => {
|
859 | // Call window.{{globals.readyCallback}} callbacks
|
860 | nuxtReady(_app)
|
861 | <% if (isDev) { %>
|
862 | // Enable hot reloading
|
863 | hotReloadAPI(_app)
|
864 | <% } %>
|
865 | })
|
866 | }
|
867 | <% if (features.transitions) { %>
|
868 | // Resolve route components
|
869 | const Components = await Promise.all(resolveComponents(router))
|
870 |
|
871 | // Enable transitions
|
872 | _app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
|
873 | if (Components.length) {
|
874 | _app.setTransitions(mapTransitions(Components, router.currentRoute))
|
875 | _lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
876 | }
|
877 | <% } else if (features.asyncData || features.fetch) { %>
|
878 | await Promise.all(resolveComponents(router))
|
879 | <% } %>
|
880 | // Initialize error handler
|
881 | _app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
882 | if (NUXT.error) {
|
883 | _app.error(NUXT.error)
|
884 | }
|
885 |
|
886 | // Add beforeEach router hooks
|
887 | router.beforeEach(loadAsyncComponents.bind(_app))
|
888 | router.beforeEach(render.bind(_app))
|
889 |
|
890 | // Fix in static: remove trailing slash to force hydration
|
891 | // Full static, if server-rendered: hydrate, to allow custom redirect to generated page
|
892 | <% if (isFullStatic) { %>
|
893 | if (NUXT.serverRendered) {
|
894 | return mount()
|
895 | }
|
896 | <% } else { %>
|
897 | // Fix in static: remove trailing slash to force hydration
|
898 | if (NUXT.serverRendered && isSamePath(NUXT.routePath, _app.context.route.path)) {
|
899 | return mount()
|
900 | }
|
901 | <% } %>
|
902 |
|
903 | // First render on client-side
|
904 | const clientFirstMount = () => {
|
905 | normalizeComponents(router.currentRoute, router.currentRoute)
|
906 | setLayoutForNextPage.call(_app, router.currentRoute)
|
907 | checkForErrors(_app)
|
908 | // Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
|
909 | mount()
|
910 | }
|
911 |
|
912 | // fix: force next tick to avoid having same timestamp when an error happen on spa fallback
|
913 | await new Promise(resolve => setTimeout(resolve, 0))
|
914 | render.call(_app, router.currentRoute, router.currentRoute, (path) => {
|
915 | // If not redirected
|
916 | if (!path) {
|
917 | clientFirstMount()
|
918 | return
|
919 | }
|
920 |
|
921 | // Add a one-time afterEach hook to
|
922 | // mount the app wait for redirect and route gets resolved
|
923 | const unregisterHook = router.afterEach((to, from) => {
|
924 | unregisterHook()
|
925 | clientFirstMount()
|
926 | })
|
927 |
|
928 | // Push the path and let route to be resolved
|
929 | router.push(path, undefined, (err) => {
|
930 | if (err) {
|
931 | errorHandler(err)
|
932 | }
|
933 | })
|
934 | })
|
935 | }
|