diff --git a/packages/config/src/config/_app.js b/packages/config/src/config/_app.js index 767fefc82b..5e62c95c19 100644 --- a/packages/config/src/config/_app.js +++ b/packages/config/src/config/_app.js @@ -69,7 +69,6 @@ export default () => ({ fetch: true, clientOnline: true, clientPrefetch: true, - clientUseUrl: false, componentAliases: true, componentClientOnly: true } diff --git a/packages/config/test/__snapshots__/options.test.js.snap b/packages/config/test/__snapshots__/options.test.js.snap index 8054727d5c..9480f671e4 100644 --- a/packages/config/test/__snapshots__/options.test.js.snap +++ b/packages/config/test/__snapshots__/options.test.js.snap @@ -189,7 +189,6 @@ Object { "asyncData": true, "clientOnline": true, "clientPrefetch": true, - "clientUseUrl": false, "componentAliases": true, "componentClientOnly": true, "deprecations": true, diff --git a/packages/config/test/config/__snapshots__/index.test.js.snap b/packages/config/test/config/__snapshots__/index.test.js.snap index 00bf216544..f9727735ef 100644 --- a/packages/config/test/config/__snapshots__/index.test.js.snap +++ b/packages/config/test/config/__snapshots__/index.test.js.snap @@ -164,7 +164,6 @@ Object { "asyncData": true, "clientOnline": true, "clientPrefetch": true, - "clientUseUrl": false, "componentAliases": true, "componentClientOnly": true, "deprecations": true, @@ -550,7 +549,6 @@ Object { "asyncData": true, "clientOnline": true, "clientPrefetch": true, - "clientUseUrl": false, "componentAliases": true, "componentClientOnly": true, "deprecations": true, diff --git a/packages/types/config/features.d.ts b/packages/types/config/features.d.ts index 93155d1f8a..d7a4d1740a 100644 --- a/packages/types/config/features.d.ts +++ b/packages/types/config/features.d.ts @@ -2,6 +2,9 @@ export interface NuxtOptionsFeatures { asyncData?: boolean clientOnline?: boolean clientPrefetch?: boolean + /** + * @deprecated + */ clientUseUrl?: boolean componentAliases?: boolean componentClientOnly?: boolean diff --git a/packages/utils/src/lang.js b/packages/utils/src/lang.js index 7ecf3fee94..ac9c3931c9 100644 --- a/packages/utils/src/lang.js +++ b/packages/utils/src/lang.js @@ -1,3 +1,5 @@ +import { joinURL } from 'ufo' + export const encodeHtml = function encodeHtml (str) { return str.replace(//g, '>') } @@ -12,13 +14,7 @@ export const isUrl = function isUrl (url) { return ['http', '//'].some(str => url.startsWith(str)) } -export const urlJoin = function urlJoin () { - return [].slice - .call(arguments) - .join('/') - .replace(/\/+/g, '/') - .replace(':/', '://') -} +export const urlJoin = joinURL /** * Wraps value in array if it is not already an array diff --git a/packages/vue-app/template/server.js b/packages/vue-app/template/server.js index e424c19e5c..ac343b84c4 100644 --- a/packages/vue-app/template/server.js +++ b/packages/vue-app/template/server.js @@ -1,6 +1,5 @@ -import { stringify } from 'querystring' import Vue from 'vue' -import { normalizeURL, joinURL } from 'ufo' +import { joinURL, normalizeURL, withQuery } from 'ufo' <% if (fetch.server) { %>import fetch from 'node-fetch'<% } %> <% if (features.middleware) { %>import middleware from './middleware.js'<% } %> import { @@ -58,20 +57,19 @@ const createNext = ssrContext => (opts) => { ssrContext.nuxt.serverRendered = false return } - opts.query = stringify(opts.query) - opts.path = opts.path + (opts.query ? '?' + opts.query : '') + let fullPath = withQuery(opts.path, opts.query) const $config = ssrContext.runtimeConfig || {} const routerBase = ($config.app && $config.app.basePath) || '<%= router.base %>' - if (!opts.path.startsWith('http') && (routerBase !== '/' && !opts.path.startsWith(routerBase))) { - opts.path = joinURL(routerBase, opts.path) + if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) { + fullPath = joinURL(routerBase, fullPath) } // Avoid loop redirect - if (decodeURI(opts.path) === decodeURI(ssrContext.url)) { + if (decodeURI(fullPath) === decodeURI(ssrContext.url)) { ssrContext.redirected = false return } ssrContext.res.writeHead(opts.status, { - Location: normalizeURL(opts.path) + Location: normalizeURL(fullPath) }) ssrContext.res.end() } diff --git a/packages/vue-app/template/utils.js b/packages/vue-app/template/utils.js index d156f77817..01c4afacc2 100644 --- a/packages/vue-app/template/utils.js +++ b/packages/vue-app/template/utils.js @@ -1,5 +1,5 @@ import Vue from 'vue' -import { normalizeURL } from 'ufo' +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) @@ -219,7 +219,7 @@ export async function setContext (app, context) { status }) } else { - path = formatUrl(path, query) + path = withQuery(path, query) if (process.server) { app.context.next({ path, @@ -594,86 +594,6 @@ function flags (options) { return options && options.sensitive ? '' : 'i' } -/** - * Format given url, append query to url query string - * - * @param {string} url - * @param {string} query - * @return {string} - */ -function formatUrl (url, query) { - <% if (features.clientUseUrl) { %> - url = new URL(url, top.location.href) - for (const key in query) { - const value = query[key] - if (value == null) { - continue - } - if (Array.isArray(value)) { - for (const arrayValue of value) { - url.searchParams.append(key, arrayValue) - } - continue - } - url.searchParams.append(key, value) - } - url.searchParams.sort() - return url.toString() - <% } else { %> - let protocol - const index = url.indexOf('://') - if (index !== -1) { - protocol = url.substring(0, index) - url = url.substring(index + 3) - } else if (url.startsWith('//')) { - url = url.substring(2) - } - - let parts = url.split('/') - let result = (protocol ? protocol + '://' : '//') + parts.shift() - - let path = parts.join('/') - if (path === '' && parts.length === 1) { - result += '/' - } - - let hash - parts = path.split('#') - if (parts.length === 2) { - [path, hash] = parts - } - - result += path ? '/' + path : '' - - if (query && JSON.stringify(query) !== '{}') { - result += (url.split('?').length === 2 ? '&' : '?') + formatQuery(query) - } - result += hash ? '#' + hash : '' - - return result - <% } %> -} -<% if (!features.clientUseUrl) { %> -/** - * Transform data object to query string - * - * @param {object} query - * @return {string} - */ -function formatQuery (query) { - return Object.keys(query).sort().map((key) => { - const val = query[key] - if (val == null) { - return '' - } - if (Array.isArray(val)) { - return val.slice().map(val2 => [key, '=', val2].join('')).join('&') - } - return key + '=' + val - }).filter(Boolean).join('&') -} -<% } %> - export function addLifecycleHook(vm, hook, fn) { if (!vm.$options[hook]) { vm.$options[hook] = [] @@ -683,21 +603,11 @@ export function addLifecycleHook(vm, hook, fn) { } } -export function urlJoin () { - return [].slice - .call(arguments) - .join('/') - .replace(/\/+/g, '/') - .replace(':/', '://') -} +export const urlJoin = joinURL -export function stripTrailingSlash (path) { - return path.replace(/\/+$/, '') || '/' -} +export const stripTrailingSlash = withoutTrailingSlash -export function isSamePath (p1, p2) { - return stripTrailingSlash(p1) === stripTrailingSlash(p2) -} +export const isSamePath = _isSamePath export function setScrollRestoration (newVal) { try {