diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index 6735c84b02..88a0081bc1 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -67,9 +67,8 @@ export function defineNuxtLink (options: NuxtLinkOptions) { return to } - const normalizeTrailingSlash = options.trailingSlash === 'append' ? withTrailingSlash : withoutTrailingSlash if (typeof to === 'string') { - return normalizeTrailingSlash(to, true) + return applyTrailingSlashBehavior(to, options.trailingSlash) } const path = 'path' in to ? to.path : resolve(to).path @@ -77,7 +76,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) { return { ...to, name: undefined, // named routes would otherwise always override trailing slash behavior - path: normalizeTrailingSlash(path, true) + path: applyTrailingSlashBehavior(path, options.trailingSlash) } } @@ -345,6 +344,17 @@ export function defineNuxtLink (options: NuxtLinkOptions) { export default defineNuxtLink(nuxtLinkDefaults) +// -- NuxtLink utils -- +function applyTrailingSlashBehavior (to: string, trailingSlash: NuxtLinkOptions['trailingSlash']): string { + const normalizeFn = trailingSlash === 'append' ? withTrailingSlash : withoutTrailingSlash + // Until https://github.com/unjs/ufo/issues/189 is resolved + const hasProtocolDifferentFromHttp = hasProtocol(to) && !to.startsWith('http') + if (hasProtocolDifferentFromHttp) { + return to + } + return normalizeFn(to, true) +} + // --- Prefetching utils --- type CallbackFn = () => void type ObserveFn = (element: Element, callback: CallbackFn) => () => void diff --git a/packages/nuxt/test/nuxt-link.test.ts b/packages/nuxt/test/nuxt-link.test.ts index 6cf247886d..a7dacb916c 100644 --- a/packages/nuxt/test/nuxt-link.test.ts +++ b/packages/nuxt/test/nuxt-link.test.ts @@ -263,10 +263,15 @@ describe('nuxt-link:propsOrAttributes', () => { expect(nuxtLink({ to: '/to' }, appendSlashOptions).props.to).toEqual('/to/') expect(nuxtLink({ to: '/to/' }, appendSlashOptions).props.to).toEqual('/to/') + expect(nuxtLink({ to: '/to#abc' }, appendSlashOptions).props.to).toEqual('/to/#abc') expect(nuxtLink({ to: { name: 'to' } }, appendSlashOptions).props.to).toHaveProperty('path', '/to/') expect(nuxtLink({ to: { path: '/to' } }, appendSlashOptions).props.to).toHaveProperty('path', '/to/') + expect(nuxtLink({ to: { path: '/to#abc' } }, appendSlashOptions).props.to).toHaveProperty('path', '/to/#abc') expect(nuxtLink({ href: '/to' }, appendSlashOptions).props.to).toEqual('/to/') + expect(nuxtLink({ href: '/to#abc' }, appendSlashOptions).props.to).toEqual('/to/#abc') expect(nuxtLink({ to: '/to?param=1' }, appendSlashOptions).props.to).toEqual('/to/?param=1') + expect(nuxtLink({ to: '/to?param=1#abc' }, appendSlashOptions).props.to).toEqual('/to/?param=1#abc') + expect(nuxtLink({ href: 'mailto:test@example.com' }, appendSlashOptions).props.href).toEqual('mailto:test@example.com') }) it('remove slash', () => { @@ -274,10 +279,14 @@ describe('nuxt-link:propsOrAttributes', () => { expect(nuxtLink({ to: '/to' }, removeSlashOptions).props.to).toEqual('/to') expect(nuxtLink({ to: '/to/' }, removeSlashOptions).props.to).toEqual('/to') + expect(nuxtLink({ to: '/to/#abc' }, removeSlashOptions).props.to).toEqual('/to#abc') expect(nuxtLink({ to: { name: 'to' } }, removeSlashOptions).props.to).toHaveProperty('path', '/to') expect(nuxtLink({ to: { path: '/to/' } }, removeSlashOptions).props.to).toHaveProperty('path', '/to') + expect(nuxtLink({ to: { path: '/to/#abc' } }, removeSlashOptions).props.to).toHaveProperty('path', '/to#abc') expect(nuxtLink({ href: '/to/' }, removeSlashOptions).props.to).toEqual('/to') expect(nuxtLink({ to: '/to/?param=1' }, removeSlashOptions).props.to).toEqual('/to?param=1') + expect(nuxtLink({ to: '/to/?param=1#abc' }, removeSlashOptions).props.to).toEqual('/to?param=1#abc') + expect(nuxtLink({ href: 'mailto:test@example.com' }, removeSlashOptions).props.href).toEqual('mailto:test@example.com') }) }) })