1 | import Vue from 'vue'
|
2 |
|
3 |
|
4 |
|
5 | if (process.client) {
|
6 | window.<%= globals.readyCallback %>Cbs = []
|
7 | window.<%= globals.readyCallback %> = (cb) => {
|
8 | window.<%= globals.readyCallback %>Cbs.push(cb)
|
9 | }
|
10 | }
|
11 |
|
12 | export function empty () {}
|
13 |
|
14 | export function globalHandleError (error) {
|
15 | if (Vue.config.errorHandler) {
|
16 | Vue.config.errorHandler(error)
|
17 | }
|
18 | }
|
19 |
|
20 | export function interopDefault (promise) {
|
21 | return promise.then(m => m.default || m)
|
22 | }
|
23 |
|
24 | <% if (features.fetch) { %>
|
25 | export function hasFetch(vm) {
|
26 | return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length
|
27 | }
|
28 | export function getChildrenComponentInstancesUsingFetch(vm, instances = []) {
|
29 | const children = vm.$children || []
|
30 | for (const child of children) {
|
31 | if (child.$fetch) {
|
32 | instances.push(child)
|
33 | continue;
|
34 | }
|
35 | if (child.$children) {
|
36 | getChildrenComponentInstancesUsingFetch(child, instances)
|
37 | }
|
38 | }
|
39 | return instances
|
40 | }
|
41 | <% } %>
|
42 | <% if (features.asyncData) { %>
|
43 | export function applyAsyncData (Component, asyncData) {
|
44 | if (
|
45 |
|
46 |
|
47 | !asyncData && Component.options.__hasNuxtData
|
48 | ) {
|
49 | return
|
50 | }
|
51 |
|
52 | const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
|
53 | Component.options._originDataFn = ComponentData
|
54 |
|
55 | Component.options.data = function () {
|
56 | const data = ComponentData.call(this, this)
|
57 | if (this.$ssrContext) {
|
58 | asyncData = this.$ssrContext.asyncData[Component.cid]
|
59 | }
|
60 | return { ...data, ...asyncData }
|
61 | }
|
62 |
|
63 | Component.options.__hasNuxtData = true
|
64 |
|
65 | if (Component._Ctor && Component._Ctor.options) {
|
66 | Component._Ctor.options.data = Component.options.data
|
67 | }
|
68 | }
|
69 | <% } %>
|
70 |
|
71 | export function sanitizeComponent (Component) {
|
72 |
|
73 | if (Component.options && Component._Ctor === Component) {
|
74 | return Component
|
75 | }
|
76 | if (!Component.options) {
|
77 | Component = Vue.extend(Component)
|
78 | Component._Ctor = Component
|
79 | } else {
|
80 | Component._Ctor = Component
|
81 | Component.extendOptions = Component.options
|
82 | }
|
83 |
|
84 | if (!Component.options.name && Component.options.__file) {
|
85 | Component.options.name = Component.options.__file
|
86 | }
|
87 | return Component
|
88 | }
|
89 |
|
90 | export function getMatchedComponents (route, matches = false, prop = 'components') {
|
91 | return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
92 | return Object.keys(m[prop]).map((key) => {
|
93 | matches && matches.push(index)
|
94 | return m[prop][key]
|
95 | })
|
96 | }))
|
97 | }
|
98 |
|
99 | export function getMatchedComponentsInstances (route, matches = false) {
|
100 | return getMatchedComponents(route, matches, 'instances')
|
101 | }
|
102 |
|
103 | export function flatMapComponents (route, fn) {
|
104 | return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
105 | return Object.keys(m.components).reduce((promises, key) => {
|
106 | if (m.components[key]) {
|
107 | promises.push(fn(m.components[key], m.instances[key], m, key, index))
|
108 | } else {
|
109 | delete m.components[key]
|
110 | }
|
111 | return promises
|
112 | }, [])
|
113 | }))
|
114 | }
|
115 |
|
116 | export function resolveRouteComponents (route, fn) {
|
117 | return Promise.all(
|
118 | flatMapComponents(route, async (Component, instance, match, key) => {
|
119 |
|
120 | if (typeof Component === 'function' && !Component.options) {
|
121 | Component = await Component()
|
122 | }
|
123 | match.components[key] = Component = sanitizeComponent(Component)
|
124 | return typeof fn === 'function' ? fn(Component, instance, match, key) : Component
|
125 | })
|
126 | )
|
127 | }
|
128 |
|
129 | export async function getRouteData (route) {
|
130 | if (!route) {
|
131 | return
|
132 | }
|
133 |
|
134 | await resolveRouteComponents(route)
|
135 |
|
136 | return {
|
137 | ...route,
|
138 | meta: getMatchedComponents(route).map((Component, index) => {
|
139 | return { ...Component.options.meta, ...(route.matched[index] || {}).meta }
|
140 | })
|
141 | }
|
142 | }
|
143 |
|
144 | export async function setContext (app, context) {
|
145 |
|
146 | if (!app.context) {
|
147 | app.context = {
|
148 | isStatic: process.static,
|
149 | isDev: <%= isDev %>,
|
150 | isHMR: false,
|
151 | app,
|
152 | <%= (store ? 'store: app.store,' : '') %>
|
153 | payload: context.payload,
|
154 | error: context.error,
|
155 | base: '<%= router.base %>',
|
156 | env: <%= JSON.stringify(env) %><%= isTest ? '// eslint-disable-line' : '' %>
|
157 | }
|
158 | // Only set once
|
159 | if (!process.static && context.req) {
|
160 | app.context.req = context.req
|
161 | }
|
162 | if (!process.static && context.res) {
|
163 | app.context.res = context.res
|
164 | }
|
165 | if (context.ssrContext) {
|
166 | app.context.ssrContext = context.ssrContext
|
167 | }
|
168 | app.context.redirect = (status, path, query) => {
|
169 | if (!status) {
|
170 | return
|
171 | }
|
172 | app.context._redirected = true
|
173 | // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
174 | let pathType = typeof path
|
175 | if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
|
176 | query = path || {}
|
177 | path = status
|
178 | pathType = typeof path
|
179 | status = 302
|
180 | }
|
181 | if (pathType === 'object') {
|
182 | path = app.router.resolve(path).route.fullPath
|
183 | }
|
184 | // "/absolute/route", "./relative/route" or "../relative/route"
|
185 | if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
|
186 | app.context.next({
|
187 | path,
|
188 | query,
|
189 | status
|
190 | })
|
191 | } else {
|
192 | path = formatUrl(path, query)
|
193 | if (process.server) {
|
194 | app.context.next({
|
195 | path,
|
196 | status
|
197 | })
|
198 | }
|
199 | if (process.client) {
|
200 | // https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
|
201 | window.location.replace(path)
|
202 |
|
203 | // Throw a redirect error
|
204 | throw new Error('ERR_REDIRECT')
|
205 | }
|
206 | }
|
207 | }
|
208 | if (process.server) {
|
209 | app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
|
210 | }
|
211 | if (process.client) {
|
212 | app.context.nuxtState = window.<%= globals.context %>
|
213 | }
|
214 | }
|
215 |
|
216 | // Dynamic keys
|
217 | const [currentRouteData, fromRouteData] = await Promise.all([
|
218 | getRouteData(context.route),
|
219 | getRouteData(context.from)
|
220 | ])
|
221 |
|
222 | if (context.route) {
|
223 | app.context.route = currentRouteData
|
224 | }
|
225 |
|
226 | if (context.from) {
|
227 | app.context.from = fromRouteData
|
228 | }
|
229 |
|
230 | app.context.next = context.next
|
231 | app.context._redirected = false
|
232 | app.context._errored = false
|
233 | app.context.isHMR = <% if(isDev) { %>Boolean(context.isHMR)<% } else { %>false<% } %>
|
234 | app.context.params = app.context.route.params || {}
|
235 | app.context.query = app.context.route.query || {}
|
236 | }
|
237 | <% if (features.middleware) { %>
|
238 | export function middlewareSeries (promises, appContext) {
|
239 | if (!promises.length || appContext._redirected || appContext._errored) {
|
240 | return Promise.resolve()
|
241 | }
|
242 | return promisify(promises[0], appContext)
|
243 | .then(() => {
|
244 | return middlewareSeries(promises.slice(1), appContext)
|
245 | })
|
246 | }
|
247 | <% } %>
|
248 | export function promisify (fn, context) {
|
249 | <% if (features.deprecations) { %>
|
250 | let promise
|
251 | if (fn.length === 2) {
|
252 | <% if (isDev) { %>
|
253 | console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' +
|
254 | 'Please switch to promises or async/await syntax')
|
255 | <% } %>
|
256 |
|
257 | // fn(context, callback)
|
258 | promise = new Promise((resolve) => {
|
259 | fn(context, function (err, data) {
|
260 | if (err) {
|
261 | context.error(err)
|
262 | }
|
263 | data = data || {}
|
264 | resolve(data)
|
265 | })
|
266 | })
|
267 | } else {
|
268 | promise = fn(context)
|
269 | }
|
270 | <% } else { %>
|
271 | const promise = fn(context)
|
272 | <% } %>
|
273 | if (promise && promise instanceof Promise && typeof promise.then === 'function') {
|
274 | return promise
|
275 | }
|
276 | return Promise.resolve(promise)
|
277 | }
|
278 |
|
279 | // Imported from vue-router
|
280 | export function getLocation (base, mode) {
|
281 | let path = decodeURI(window.location.pathname)
|
282 | if (mode === 'hash') {
|
283 | return window.location.hash.replace(/^#\//, '')
|
284 | }
|
285 | // To get matched with sanitized router.base add trailing slash
|
286 | if (base && (path.endsWith('/') ? path : path + '/').startsWith(base)) {
|
287 | path = path.slice(base.length)
|
288 | }
|
289 | return (path || '/') + window.location.search + window.location.hash
|
290 | }
|
291 |
|
292 | // Imported from path-to-regexp
|
293 |
|
294 | /**
|
295 | * Compile a string to a template function for the path.
|
296 | *
|
297 | * @param {string} str
|
298 | * @param {Object=} options
|
299 | * @return {!function(Object=, Object=)}
|
300 | */
|
301 | export function compile (str, options) {
|
302 | return tokensToFunction(parse(str, options), options)
|
303 | }
|
304 |
|
305 | export function getQueryDiff (toQuery, fromQuery) {
|
306 | const diff = {}
|
307 | const queries = { ...toQuery, ...fromQuery }
|
308 | for (const k in queries) {
|
309 | if (String(toQuery[k]) !== String(fromQuery[k])) {
|
310 | diff[k] = true
|
311 | }
|
312 | }
|
313 | return diff
|
314 | }
|
315 |
|
316 | export function normalizeError (err) {
|
317 | let message
|
318 | if (!(err.message || typeof err === 'string')) {
|
319 | try {
|
320 | message = JSON.stringify(err, null, 2)
|
321 | } catch (e) {
|
322 | message = `[${err.constructor.name}]`
|
323 | }
|
324 | } else {
|
325 | message = err.message || err
|
326 | }
|
327 | return {
|
328 | ...err,
|
329 | message,
|
330 | statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
|
331 | }
|
332 | }
|
333 |
|
334 | /**
|
335 | * The main path matching regexp utility.
|
336 | *
|
337 | * @type {RegExp}
|
338 | */
|
339 | const PATH_REGEXP = new RegExp([
|
340 | // Match escaped characters that would otherwise appear in future matches.
|
341 | // This allows the user to escape special characters that won't transform.
|
342 | '(\\\\.)',
|
343 | // Match Express-style parameters and un-named parameters with a prefix
|
344 | // and optional suffixes. Matches appear as:
|
345 | //
|
346 | // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
|
347 | // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
|
348 | // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
|
349 | '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
|
350 | ].join('|'), 'g')
|
351 |
|
352 | /**
|
353 | * Parse a string for the raw tokens.
|
354 | *
|
355 | * @param {string} str
|
356 | * @param {Object=} options
|
357 | * @return {!Array}
|
358 | */
|
359 | function parse (str, options) {
|
360 | const tokens = []
|
361 | let key = 0
|
362 | let index = 0
|
363 | let path = ''
|
364 | const defaultDelimiter = (options && options.delimiter) || '/'
|
365 | let res
|
366 |
|
367 | while ((res = PATH_REGEXP.exec(str)) != null) {
|
368 | const m = res[0]
|
369 | const escaped = res[1]
|
370 | const offset = res.index
|
371 | path += str.slice(index, offset)
|
372 | index = offset + m.length
|
373 |
|
374 | // Ignore already escaped sequences.
|
375 | if (escaped) {
|
376 | path += escaped[1]
|
377 | continue
|
378 | }
|
379 |
|
380 | const next = str[index]
|
381 | const prefix = res[2]
|
382 | const name = res[3]
|
383 | const capture = res[4]
|
384 | const group = res[5]
|
385 | const modifier = res[6]
|
386 | const asterisk = res[7]
|
387 |
|
388 | // Push the current path onto the tokens.
|
389 | if (path) {
|
390 | tokens.push(path)
|
391 | path = ''
|
392 | }
|
393 |
|
394 | const partial = prefix != null && next != null && next !== prefix
|
395 | const repeat = modifier === '+' || modifier === '*'
|
396 | const optional = modifier === '?' || modifier === '*'
|
397 | const delimiter = res[2] || defaultDelimiter
|
398 | const pattern = capture || group
|
399 |
|
400 | tokens.push({
|
401 | name: name || key++,
|
402 | prefix: prefix || '',
|
403 | delimiter,
|
404 | optional,
|
405 | repeat,
|
406 | partial,
|
407 | asterisk: Boolean(asterisk),
|
408 | pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
|
409 | })
|
410 | }
|
411 |
|
412 | // Match any characters still remaining.
|
413 | if (index < str.length) {
|
414 | path += str.substr(index)
|
415 | }
|
416 |
|
417 | // If the path exists, push it onto the end.
|
418 | if (path) {
|
419 | tokens.push(path)
|
420 | }
|
421 |
|
422 | return tokens
|
423 | }
|
424 |
|
425 | /**
|
426 | * Prettier encoding of URI path segments.
|
427 | *
|
428 | * @param {string}
|
429 | * @return {string}
|
430 | */
|
431 | function encodeURIComponentPretty (str, slashAllowed) {
|
432 | const re = slashAllowed ? /[?#]/g : /[/?#]/g
|
433 | return encodeURI(str).replace(re, (c) => {
|
434 | return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
435 | })
|
436 | }
|
437 |
|
438 | /**
|
439 | * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
|
440 | *
|
441 | * @param {string}
|
442 | * @return {string}
|
443 | */
|
444 | function encodeAsterisk (str) {
|
445 | return encodeURIComponentPretty(str, true)
|
446 | }
|
447 |
|
448 | /**
|
449 | * Escape a regular expression string.
|
450 | *
|
451 | * @param {string} str
|
452 | * @return {string}
|
453 | */
|
454 | function escapeString (str) {
|
455 | return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
|
456 | }
|
457 |
|
458 | /**
|
459 | * Escape the capturing group by escaping special characters and meaning.
|
460 | *
|
461 | * @param {string} group
|
462 | * @return {string}
|
463 | */
|
464 | function escapeGroup (group) {
|
465 | return group.replace(/([=!:$/()])/g, '\\$1')
|
466 | }
|
467 |
|
468 | /**
|
469 | * Expose a method for transforming tokens into the path function.
|
470 | */
|
471 | function tokensToFunction (tokens, options) {
|
472 | // Compile all the tokens into regexps.
|
473 | const matches = new Array(tokens.length)
|
474 |
|
475 | // Compile all the patterns before compilation.
|
476 | for (let i = 0; i < tokens.length; i++) {
|
477 | if (typeof tokens[i] === 'object') {
|
478 | matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
|
479 | }
|
480 | }
|
481 |
|
482 | return function (obj, opts) {
|
483 | let path = ''
|
484 | const data = obj || {}
|
485 | const options = opts || {}
|
486 | const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
|
487 |
|
488 | for (let i = 0; i < tokens.length; i++) {
|
489 | const token = tokens[i]
|
490 |
|
491 | if (typeof token === 'string') {
|
492 | path += token
|
493 |
|
494 | continue
|
495 | }
|
496 |
|
497 | const value = data[token.name || 'pathMatch']
|
498 | let segment
|
499 |
|
500 | if (value == null) {
|
501 | if (token.optional) {
|
502 | // Prepend partial segment prefixes.
|
503 | if (token.partial) {
|
504 | path += token.prefix
|
505 | }
|
506 |
|
507 | continue
|
508 | } else {
|
509 | throw new TypeError('Expected "' + token.name + '" to be defined')
|
510 | }
|
511 | }
|
512 |
|
513 | if (Array.isArray(value)) {
|
514 | if (!token.repeat) {
|
515 | throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
|
516 | }
|
517 |
|
518 | if (value.length === 0) {
|
519 | if (token.optional) {
|
520 | continue
|
521 | } else {
|
522 | throw new TypeError('Expected "' + token.name + '" to not be empty')
|
523 | }
|
524 | }
|
525 |
|
526 | for (let j = 0; j < value.length; j++) {
|
527 | segment = encode(value[j])
|
528 |
|
529 | if (!matches[i].test(segment)) {
|
530 | throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
|
531 | }
|
532 |
|
533 | path += (j === 0 ? token.prefix : token.delimiter) + segment
|
534 | }
|
535 |
|
536 | continue
|
537 | }
|
538 |
|
539 | segment = token.asterisk ? encodeAsterisk(value) : encode(value)
|
540 |
|
541 | if (!matches[i].test(segment)) {
|
542 | throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
|
543 | }
|
544 |
|
545 | path += token.prefix + segment
|
546 | }
|
547 |
|
548 | return path
|
549 | }
|
550 | }
|
551 |
|
552 | /**
|
553 | * Get the flags for a regexp from the options.
|
554 | *
|
555 | * @param {Object} options
|
556 | * @return {string}
|
557 | */
|
558 | function flags (options) {
|
559 | return options && options.sensitive ? '' : 'i'
|
560 | }
|
561 |
|
562 | /**
|
563 | * Format given url, append query to url query string
|
564 | *
|
565 | * @param {string} url
|
566 | * @param {string} query
|
567 | * @return {string}
|
568 | */
|
569 | function formatUrl (url, query) {
|
570 | <% if (features.clientUseUrl) { %>
|
571 | url = new URL(url, top.location.href)
|
572 | for (const key in query) {
|
573 | const value = query[key]
|
574 | if (value == null) {
|
575 | continue
|
576 | }
|
577 | if (Array.isArray(value)) {
|
578 | for (const arrayValue of value) {
|
579 | url.searchParams.append(key, arrayValue)
|
580 | }
|
581 | continue
|
582 | }
|
583 | url.searchParams.append(key, value)
|
584 | }
|
585 | url.searchParams.sort()
|
586 | return url.toString()
|
587 | <% } else { %>
|
588 | let protocol
|
589 | const index = url.indexOf('://')
|
590 | if (index !== -1) {
|
591 | protocol = url.substring(0, index)
|
592 | url = url.substring(index + 3)
|
593 | } else if (url.startsWith('//')) {
|
594 | url = url.substring(2)
|
595 | }
|
596 |
|
597 | let parts = url.split('/')
|
598 | let result = (protocol ? protocol + '://' : '//') + parts.shift()
|
599 |
|
600 | let path = parts.join('/')
|
601 | if (path === '' && parts.length === 1) {
|
602 | result += '/'
|
603 | }
|
604 |
|
605 | let hash
|
606 | parts = path.split('#')
|
607 | if (parts.length === 2) {
|
608 | [path, hash] = parts
|
609 | }
|
610 |
|
611 | result += path ? '/' + path : ''
|
612 |
|
613 | if (query && JSON.stringify(query) !== '{}') {
|
614 | result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query)
|
615 | }
|
616 | result += hash ? '#' + hash : ''
|
617 |
|
618 | return result
|
619 | <% } %>
|
620 | }
|
621 | <% if (!features.clientUseUrl) { %>
|
622 | /**
|
623 | * Transform data object to query string
|
624 | *
|
625 | * @param {object} query
|
626 | * @return {string}
|
627 | */
|
628 | function formatQuery (query) {
|
629 | return Object.keys(query).sort().map((key) => {
|
630 | const val = query[key]
|
631 | if (val == null) {
|
632 | return ''
|
633 | }
|
634 | if (Array.isArray(val)) {
|
635 | return val.slice().map(val2 => [key, '=', val2].join('')).join('&')
|
636 | }
|
637 | return key + '=' + val
|
638 | }).filter(Boolean).join('&')
|
639 | }
|
640 | <% } %>
|
641 |
|
642 | export function addLifecycleHook(vm, hook, fn) {
|
643 | if (!vm.$options[hook]) {
|
644 | vm.$options[hook] = []
|
645 | }
|
646 | if (!vm.$options[hook].includes(fn)) {
|
647 | vm.$options[hook].push(fn)
|
648 | }
|
649 | }
|
650 |
|
651 | export function urlJoin () {
|
652 | return [].slice
|
653 | .call(arguments)
|
654 | .join('/')
|
655 | .replace(/\/+/g, '/')
|
656 | .replace(':/', '://')
|
657 | }
|
658 |
|
659 | export function stripTrailingSlash (path) {
|
660 | return path.replace(/\/+$/, '') || '/'
|
661 | }
|
662 |
|
663 | export function isSamePath (p1, p2) {
|
664 | return stripTrailingSlash(p1) === stripTrailingSlash(p2)
|
665 | }
|
666 |
|
\ | No newline at end of file |