import Vue from 'vue' const noopData = () => ({}) // window.onNuxtReady(() => console.log('Ready')) hook // Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading) if (process.browser) { window._nuxtReadyCbs = [] window.onNuxtReady = function (cb) { window._nuxtReadyCbs.push(cb) } } export function applyAsyncData (Component, asyncData) { const ComponentData = Component.options.data || noopData // Prevent calling this method for each request on SSR context if (!asyncData && Component.options.hasAsyncData) { return } Component.options.hasAsyncData = true Component.options.data = function () { const data = ComponentData.call(this) if (this.$ssrContext) { asyncData = this.$ssrContext.asyncData[Component.cid] } return { ...data, ...asyncData } } if (Component._Ctor && Component._Ctor.options) { Component._Ctor.options.data = Component.options.data } } export function sanitizeComponent (Component) { if (!Component.options) { Component = Vue.extend(Component) // fix issue #6 Component._Ctor = Component } else { Component._Ctor = Component Component.extendOptions = Component.options } // For debugging purpose if (!Component.options.name && Component.options.__file) { Component.options.name = Component.options.__file } return Component } export function getMatchedComponents (route) { return [].concat.apply([], route.matched.map(function (m) { return Object.keys(m.components).map(function (key) { return m.components[key] }) })) } export function getMatchedComponentsInstances (route) { return [].concat.apply([], route.matched.map(function (m) { return Object.keys(m.instances).map(function (key) { return m.instances[key] }) })) } export function flatMapComponents (route, fn) { return Array.prototype.concat.apply([], route.matched.map(function (m, index) { return Object.keys(m.components).map(function (key) { return fn(m.components[key], m.instances[key], m, key, index) }) })) } export function getContext (context, app) { let ctx = { isServer: !!context.isServer, isClient: !!context.isClient, isStatic: process.static, isDev: <%= isDev %>, isHMR: context.isHMR || false, app: app, <%= (store ? 'store: context.store,' : '') %> route: (context.to ? context.to : context.route), payload: context.payload, error: context.error, base: '<%= router.base %>', env: <%= JSON.stringify(env) %> } const next = context.next ctx.params = ctx.route.params || {} ctx.query = ctx.route.query || {} ctx.redirect = function (status, path, query) { if (!status) return ctx._redirected = true // Used in middleware // if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' }) if (typeof status === 'string' && (typeof path === 'undefined' || typeof path === 'object')) { query = path || {} path = status status = 302 } next({ path: path, query: query, status: status }) } if (context.req) ctx.req = context.req if (context.res) ctx.res = context.res if (context.from) ctx.from = context.from if (ctx.isServer && context.beforeRenderFns) { ctx.beforeNuxtRender = (fn) => context.beforeRenderFns.push(fn) } if (ctx.isClient && window.__NUXT__) { ctx.serverState = window.__NUXT__ } return ctx } export function middlewareSeries (promises, context) { if (!promises.length || context._redirected) { return Promise.resolve() } return promisify(promises[0], context) .then(() => { return middlewareSeries(promises.slice(1), context) }) } export function promisify (fn, context) { let promise if (fn.length === 2) { // 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'))) { promise = Promise.resolve(promise) } return promise } // Imported from vue-router export function getLocation (base, mode) { var path = window.location.pathname if (mode === 'hash') { return window.location.hash.replace(/^#\//, '') } if (base && path.indexOf(base) === 0) { path = path.slice(base.length) } return (path || '/') + window.location.search + window.location.hash } export function urlJoin () { return [].slice.call(arguments).join('/').replace(/\/+/g, '/') } // 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)) } /** * 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) { var tokens = [] var key = 0 var index = 0 var path = '' var defaultDelimiter = options && options.delimiter || '/' var res while ((res = PATH_REGEXP.exec(str)) != null) { var m = res[0] var escaped = res[1] var offset = res.index path += str.slice(index, offset) index = offset + m.length // Ignore already escaped sequences. if (escaped) { path += escaped[1] continue } var next = str[index] var prefix = res[2] var name = res[3] var capture = res[4] var group = res[5] var modifier = res[6] var asterisk = res[7] // Push the current path onto the tokens. if (path) { tokens.push(path) path = '' } var partial = prefix != null && next != null && next !== prefix var repeat = modifier === '+' || modifier === '*' var optional = modifier === '?' || modifier === '*' var delimiter = res[2] || defaultDelimiter var pattern = capture || group tokens.push({ name: name || key++, prefix: prefix || '', delimiter: delimiter, optional: optional, repeat: repeat, partial: partial, asterisk: !!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) { return encodeURI(str).replace(/[\/?#]/g, function (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 encodeURI(str).replace(/[?#]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } /** * Expose a method for transforming tokens into the path function. */ function tokensToFunction (tokens) { // Compile all the tokens into regexps. var matches = new Array(tokens.length) // Compile all the patterns before compilation. for (var i = 0; i < tokens.length; i++) { if (typeof tokens[i] === 'object') { matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$') } } return function (obj, opts) { var path = '' var data = obj || {} var options = opts || {} var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent for (var i = 0; i < tokens.length; i++) { var token = tokens[i] if (typeof token === 'string') { path += token continue } var value = data[token.name] var 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 (var 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 } } /** * 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') }