123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- import Vue from 'vue'
- import { isSamePath as _isSamePath, joinURL, normalizeURL, withQuery, withoutTrailingSlash } from 'ufo'
- // window.{{globals.loadedCallback}} hook
- // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
- if (process.client) {
- window.onNuxtReadyCbs = []
- window.onNuxtReady = (cb) => {
- window.onNuxtReadyCbs.push(cb)
- }
- }
- export function createGetCounter (counterObject, defaultKey = '') {
- return function getCounter (id = defaultKey) {
- if (counterObject[id] === undefined) {
- counterObject[id] = 0
- }
- return counterObject[id]++
- }
- }
- export function empty () {}
- export function globalHandleError (error) {
- if (Vue.config.errorHandler) {
- Vue.config.errorHandler(error)
- }
- }
- export function interopDefault (promise) {
- return promise.then(m => m.default || m)
- }
- export function hasFetch(vm) {
- return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length
- }
- export function purifyData(data) {
- if (process.env.NODE_ENV === 'production') {
- return data
- }
- return Object.entries(data).filter(
- ([key, value]) => {
- const valid = !(value instanceof Function) && !(value instanceof Promise)
- if (!valid) {
- console.warn(`${key} is not able to be stringified. This will break in a production environment.`)
- }
- return valid
- }
- ).reduce((obj, [key, value]) => {
- obj[key] = value
- return obj
- }, {})
- }
- export function getChildrenComponentInstancesUsingFetch(vm, instances = []) {
- const children = vm.$children || []
- for (const child of children) {
- if (child.$fetch) {
- instances.push(child)
- continue; // Don't get the children since it will reload the template
- }
- if (child.$children) {
- getChildrenComponentInstancesUsingFetch(child, instances)
- }
- }
- return instances
- }
- export function applyAsyncData (Component, asyncData) {
- if (
- // For SSR, we once all this function without second param to just apply asyncData
- // Prevent doing this for each SSR request
- !asyncData && Component.options.__hasNuxtData
- ) {
- return
- }
- const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
- Component.options._originDataFn = ComponentData
- Component.options.data = function () {
- const data = ComponentData.call(this, this)
- if (this.$ssrContext) {
- asyncData = this.$ssrContext.asyncData[Component.cid]
- }
- return { ...data, ...asyncData }
- }
- Component.options.__hasNuxtData = true
- if (Component._Ctor && Component._Ctor.options) {
- Component._Ctor.options.data = Component.options.data
- }
- }
- export function sanitizeComponent (Component) {
- // If Component already sanitized
- if (Component.options && Component._Ctor === Component) {
- return Component
- }
- if (!Component.options) {
- Component = Vue.extend(Component) // fix issue #6
- Component._Ctor = Component
- } else {
- Component._Ctor = Component
- Component.extendOptions = Component.options
- }
- // If no component name defined, set file path as name, (also fixes #5703)
- if (!Component.options.name && Component.options.__file) {
- Component.options.name = Component.options.__file
- }
- return Component
- }
- export function getMatchedComponents (route, matches = false, prop = 'components') {
- return Array.prototype.concat.apply([], route.matched.map((m, index) => {
- return Object.keys(m[prop]).map((key) => {
- matches && matches.push(index)
- return m[prop][key]
- })
- }))
- }
- export function getMatchedComponentsInstances (route, matches = false) {
- return getMatchedComponents(route, matches, 'instances')
- }
- export function flatMapComponents (route, fn) {
- return Array.prototype.concat.apply([], route.matched.map((m, index) => {
- return Object.keys(m.components).reduce((promises, key) => {
- if (m.components[key]) {
- promises.push(fn(m.components[key], m.instances[key], m, key, index))
- } else {
- delete m.components[key]
- }
- return promises
- }, [])
- }))
- }
- export function resolveRouteComponents (route, fn) {
- return Promise.all(
- flatMapComponents(route, async (Component, instance, match, key) => {
- // If component is a function, resolve it
- if (typeof Component === 'function' && !Component.options) {
- try {
- Component = await Component()
- } catch (error) {
- // Handle webpack chunk loading errors
- // This may be due to a new deployment or a network problem
- if (
- error &&
- error.name === 'ChunkLoadError' &&
- typeof window !== 'undefined' &&
- window.sessionStorage
- ) {
- const timeNow = Date.now()
- const previousReloadTime = parseInt(window.sessionStorage.getItem('nuxt-reload'))
- // check for previous reload time not to reload infinitely
- if (!previousReloadTime || previousReloadTime + 60000 < timeNow) {
- window.sessionStorage.setItem('nuxt-reload', timeNow)
- window.location.reload(true /* skip cache */)
- }
- }
- throw error
- }
- }
- match.components[key] = Component = sanitizeComponent(Component)
- return typeof fn === 'function' ? fn(Component, instance, match, key) : Component
- })
- )
- }
- export async function getRouteData (route) {
- if (!route) {
- return
- }
- // Make sure the components are resolved (code-splitting)
- await resolveRouteComponents(route)
- // Send back a copy of route with meta based on Component definition
- return {
- ...route,
- meta: getMatchedComponents(route).map((Component, index) => {
- return { ...Component.options.meta, ...(route.matched[index] || {}).meta }
- })
- }
- }
- export async function setContext (app, context) {
- // If context not defined, create it
- if (!app.context) {
- app.context = {
- isStatic: process.static,
- isDev: true,
- isHMR: false,
- app,
- payload: context.payload,
- error: context.error,
- base: app.router.options.base,
- env: {"BASE_URL":"localhost:8090/website/","NODE_ENV":"development"}
- }
- // Only set once
- if (context.req) {
- app.context.req = context.req
- }
- if (context.res) {
- app.context.res = context.res
- }
- if (context.ssrContext) {
- app.context.ssrContext = context.ssrContext
- }
- app.context.redirect = (status, path, query) => {
- if (!status) {
- return
- }
- app.context._redirected = true
- // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
- let pathType = typeof path
- if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
- query = path || {}
- path = status
- pathType = typeof path
- status = 302
- }
- if (pathType === 'object') {
- path = app.router.resolve(path).route.fullPath
- }
- // "/absolute/route", "./relative/route" or "../relative/route"
- if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
- app.context.next({
- path,
- query,
- status
- })
- } else {
- path = withQuery(path, query)
- if (process.server) {
- app.context.next({
- path,
- status
- })
- }
- if (process.client) {
- // https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
- window.location.replace(path)
- // Throw a redirect error
- throw new Error('ERR_REDIRECT')
- }
- }
- }
- if (process.server) {
- app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
- }
- if (process.client) {
- app.context.nuxtState = window.__NUXT__
- }
- }
- // Dynamic keys
- const [currentRouteData, fromRouteData] = await Promise.all([
- getRouteData(context.route),
- getRouteData(context.from)
- ])
- if (context.route) {
- app.context.route = currentRouteData
- }
- if (context.from) {
- app.context.from = fromRouteData
- }
- app.context.next = context.next
- app.context._redirected = false
- app.context._errored = false
- app.context.isHMR = Boolean(context.isHMR)
- app.context.params = app.context.route.params || {}
- app.context.query = app.context.route.query || {}
- }
- export function middlewareSeries (promises, appContext) {
- if (!promises.length || appContext._redirected || appContext._errored) {
- return Promise.resolve()
- }
- return promisify(promises[0], appContext)
- .then(() => {
- return middlewareSeries(promises.slice(1), appContext)
- })
- }
- export function promisify (fn, context) {
- let promise
- if (fn.length === 2) {
- console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' +
- 'Please switch to promises or async/await syntax')
- // fn(context, callback)
- promise = new Promise((resolve) => {
- fn(context, function (err, data) {
- if (err) {
- context.error(err)
- }
- data = data || {}
- resolve(data)
- })
- })
- } else {
- promise = fn(context)
- }
- if (promise && promise instanceof Promise && typeof promise.then === 'function') {
- return promise
- }
- return Promise.resolve(promise)
- }
- // Imported from vue-router
- export function getLocation (base, mode) {
- if (mode === 'hash') {
- return window.location.hash.replace(/^#\//, '')
- }
- base = decodeURI(base).slice(0, -1) // consideration is base is normalized with trailing slash
- let path = decodeURI(window.location.pathname)
- if (base && path.startsWith(base)) {
- path = path.slice(base.length)
- }
- const fullPath = (path || '/') + window.location.search + window.location.hash
- return normalizeURL(fullPath)
- }
- // Imported from path-to-regexp
- /**
- * Compile a string to a template function for the path.
- *
- * @param {string} str
- * @param {Object=} options
- * @return {!function(Object=, Object=)}
- */
- export function compile (str, options) {
- return tokensToFunction(parse(str, options), options)
- }
- export function getQueryDiff (toQuery, fromQuery) {
- const diff = {}
- const queries = { ...toQuery, ...fromQuery }
- for (const k in queries) {
- if (String(toQuery[k]) !== String(fromQuery[k])) {
- diff[k] = true
- }
- }
- return diff
- }
- export function normalizeError (err) {
- let message
- if (!(err.message || typeof err === 'string')) {
- try {
- message = JSON.stringify(err, null, 2)
- } catch (e) {
- message = `[${err.constructor.name}]`
- }
- } else {
- message = err.message || err
- }
- return {
- ...err,
- message,
- statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
- }
- }
- /**
- * The main path matching regexp utility.
- *
- * @type {RegExp}
- */
- const PATH_REGEXP = new RegExp([
- // Match escaped characters that would otherwise appear in future matches.
- // This allows the user to escape special characters that won't transform.
- '(\\\\.)',
- // Match Express-style parameters and un-named parameters with a prefix
- // and optional suffixes. Matches appear as:
- //
- // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
- // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
- // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
- '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
- ].join('|'), 'g')
- /**
- * Parse a string for the raw tokens.
- *
- * @param {string} str
- * @param {Object=} options
- * @return {!Array}
- */
- function parse (str, options) {
- const tokens = []
- let key = 0
- let index = 0
- let path = ''
- const defaultDelimiter = (options && options.delimiter) || '/'
- let res
- while ((res = PATH_REGEXP.exec(str)) != null) {
- const m = res[0]
- const escaped = res[1]
- const offset = res.index
- path += str.slice(index, offset)
- index = offset + m.length
- // Ignore already escaped sequences.
- if (escaped) {
- path += escaped[1]
- continue
- }
- const next = str[index]
- const prefix = res[2]
- const name = res[3]
- const capture = res[4]
- const group = res[5]
- const modifier = res[6]
- const asterisk = res[7]
- // Push the current path onto the tokens.
- if (path) {
- tokens.push(path)
- path = ''
- }
- const partial = prefix != null && next != null && next !== prefix
- const repeat = modifier === '+' || modifier === '*'
- const optional = modifier === '?' || modifier === '*'
- const delimiter = res[2] || defaultDelimiter
- const pattern = capture || group
- tokens.push({
- name: name || key++,
- prefix: prefix || '',
- delimiter,
- optional,
- repeat,
- partial,
- asterisk: Boolean(asterisk),
- pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
- })
- }
- // Match any characters still remaining.
- if (index < str.length) {
- path += str.substr(index)
- }
- // If the path exists, push it onto the end.
- if (path) {
- tokens.push(path)
- }
- return tokens
- }
- /**
- * Prettier encoding of URI path segments.
- *
- * @param {string}
- * @return {string}
- */
- function encodeURIComponentPretty (str, slashAllowed) {
- const re = slashAllowed ? /[?#]/g : /[/?#]/g
- return encodeURI(str).replace(re, (c) => {
- return '%' + c.charCodeAt(0).toString(16).toUpperCase()
- })
- }
- /**
- * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
- *
- * @param {string}
- * @return {string}
- */
- function encodeAsterisk (str) {
- return encodeURIComponentPretty(str, true)
- }
- /**
- * Escape a regular expression string.
- *
- * @param {string} str
- * @return {string}
- */
- function escapeString (str) {
- return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
- }
- /**
- * Escape the capturing group by escaping special characters and meaning.
- *
- * @param {string} group
- * @return {string}
- */
- function escapeGroup (group) {
- return group.replace(/([=!:$/()])/g, '\\$1')
- }
- /**
- * Expose a method for transforming tokens into the path function.
- */
- function tokensToFunction (tokens, options) {
- // Compile all the tokens into regexps.
- const matches = new Array(tokens.length)
- // Compile all the patterns before compilation.
- for (let i = 0; i < tokens.length; i++) {
- if (typeof tokens[i] === 'object') {
- matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
- }
- }
- return function (obj, opts) {
- let path = ''
- const data = obj || {}
- const options = opts || {}
- const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
- for (let i = 0; i < tokens.length; i++) {
- const token = tokens[i]
- if (typeof token === 'string') {
- path += token
- continue
- }
- const value = data[token.name || 'pathMatch']
- let segment
- if (value == null) {
- if (token.optional) {
- // Prepend partial segment prefixes.
- if (token.partial) {
- path += token.prefix
- }
- continue
- } else {
- throw new TypeError('Expected "' + token.name + '" to be defined')
- }
- }
- if (Array.isArray(value)) {
- if (!token.repeat) {
- throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
- }
- if (value.length === 0) {
- if (token.optional) {
- continue
- } else {
- throw new TypeError('Expected "' + token.name + '" to not be empty')
- }
- }
- for (let j = 0; j < value.length; j++) {
- segment = encode(value[j])
- if (!matches[i].test(segment)) {
- throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
- }
- path += (j === 0 ? token.prefix : token.delimiter) + segment
- }
- continue
- }
- segment = token.asterisk ? encodeAsterisk(value) : encode(value)
- if (!matches[i].test(segment)) {
- throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
- }
- path += token.prefix + segment
- }
- return path
- }
- }
- /**
- * Get the flags for a regexp from the options.
- *
- * @param {Object} options
- * @return {string}
- */
- function flags (options) {
- return options && options.sensitive ? '' : 'i'
- }
- export function addLifecycleHook(vm, hook, fn) {
- if (!vm.$options[hook]) {
- vm.$options[hook] = []
- }
- if (!vm.$options[hook].includes(fn)) {
- vm.$options[hook].push(fn)
- }
- }
- export const urlJoin = joinURL
- export const stripTrailingSlash = withoutTrailingSlash
- export const isSamePath = _isSamePath
- export function setScrollRestoration (newVal) {
- try {
- window.history.scrollRestoration = newVal;
- } catch(e) {}
- }
|