mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
fix(nuxt): resolve internal target: blank
links with base (#23751)
This commit is contained in:
parent
7fcdce26b2
commit
ffa6b6e60c
@ -1,12 +1,12 @@
|
||||
import type { ComputedRef, DefineComponent, InjectionKey, PropType } from 'vue'
|
||||
import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent } from 'vue'
|
||||
import type { RouteLocation, RouteLocationRaw } from '#vue-router'
|
||||
import { hasProtocol, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash } from 'ufo'
|
||||
import { hasProtocol, joinURL, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash } from 'ufo'
|
||||
|
||||
import { preloadRouteComponents } from '../composables/preload'
|
||||
import { onNuxtReady } from '../composables/ready'
|
||||
import { navigateTo, useRouter } from '../composables/router'
|
||||
import { useNuxtApp } from '../nuxt'
|
||||
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
||||
import { cancelIdleCallback, requestIdleCallback } from '../compat/idle-callback'
|
||||
|
||||
// @ts-expect-error virtual file
|
||||
@ -170,6 +170,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
},
|
||||
setup (props, { slots }) {
|
||||
const router = useRouter()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
// Resolving `to` value from `to` and `href` props
|
||||
const to: ComputedRef<string | RouteLocationRaw> = computed(() => {
|
||||
@ -180,6 +181,9 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
return resolveTrailingSlashBehavior(path, router.resolve)
|
||||
})
|
||||
|
||||
// Lazily check whether to.value has a protocol
|
||||
const isProtocolURL = computed(() => typeof to.value === 'string' && hasProtocol(to.value, { acceptRelative: true }))
|
||||
|
||||
// Resolving link type
|
||||
const isExternal = computed<boolean>(() => {
|
||||
// External prop is explicitly set
|
||||
@ -197,7 +201,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
return false
|
||||
}
|
||||
|
||||
return to.value === '' || hasProtocol(to.value, { acceptRelative: true })
|
||||
return to.value === '' || isProtocolURL.value
|
||||
})
|
||||
|
||||
// Prefetching
|
||||
@ -280,7 +284,11 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
|
||||
// Resolves `to` value if it's a route location object
|
||||
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
|
||||
const href = typeof to.value === 'object' ? router.resolve(to.value)?.href ?? null : to.value || null
|
||||
const href = typeof to.value === 'object'
|
||||
? router.resolve(to.value)?.href ?? null
|
||||
: (to.value && !props.external && !isProtocolURL.value)
|
||||
? resolveTrailingSlashBehavior(joinURL(config.app.baseURL, to.value), router.resolve) as string
|
||||
: to.value || null
|
||||
|
||||
// Resolves `target` value
|
||||
const target = props.target || null
|
||||
|
@ -2,6 +2,16 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import type { RouteLocation, RouteLocationRaw } from 'vue-router'
|
||||
import type { NuxtLinkOptions, NuxtLinkProps } from '../src/app/components/nuxt-link'
|
||||
import { defineNuxtLink } from '../src/app/components/nuxt-link'
|
||||
import { useRuntimeConfig } from '../src/app/nuxt'
|
||||
|
||||
// mocks `useRuntimeConfig()`
|
||||
vi.mock('../src/app/nuxt', () => ({
|
||||
useRuntimeConfig: vi.fn(() => ({
|
||||
app: {
|
||||
baseURL: '/'
|
||||
}
|
||||
}))
|
||||
}))
|
||||
|
||||
// Mocks `h()`
|
||||
vi.mock('vue', async () => {
|
||||
@ -125,6 +135,40 @@ describe('nuxt-link:propsOrAttributes', () => {
|
||||
it('defaults to `null`', () => {
|
||||
expect(nuxtLink({ to: 'https://nuxtjs.org' }).props.target).toBe(null)
|
||||
})
|
||||
|
||||
it('prefixes target="_blank" internal links with baseURL', () => {
|
||||
vi.mocked(useRuntimeConfig).withImplementation(() => {
|
||||
return {
|
||||
app: {
|
||||
baseURL: '/base'
|
||||
}
|
||||
} as any
|
||||
}, () => {
|
||||
expect(nuxtLink({ to: '/', target: '_blank' }).props.href).toBe('/base')
|
||||
expect(nuxtLink({ to: '/base', target: '_blank' }).props.href).toBe('/base/base')
|
||||
expect(nuxtLink({ to: '/to', target: '_blank' }).props.href).toBe('/base/to')
|
||||
expect(nuxtLink({ to: '/base/to', target: '_blank' }).props.href).toBe('/base/base/to')
|
||||
expect(nuxtLink({ to: '//base/to', target: '_blank' }).props.href).toBe('//base/to')
|
||||
expect(nuxtLink({ to: '//to.com/thing', target: '_blank' }).props.href).toBe('//to.com/thing')
|
||||
expect(nuxtLink({ to: 'https://test.com/to', target: '_blank' }).props.href).toBe('https://test.com/to')
|
||||
|
||||
expect(nuxtLink({ to: '/', target: '_blank' }, { trailingSlash: 'append' }).props.href).toBe('/base/')
|
||||
expect(nuxtLink({ to: '/base/', target: '_blank' }, { trailingSlash: 'remove' }).props.href).toBe('/base/base')
|
||||
})
|
||||
})
|
||||
|
||||
it('excludes the baseURL for external links', () => {
|
||||
vi.mocked(useRuntimeConfig).withImplementation(() => {
|
||||
return {
|
||||
app: {
|
||||
baseURL: '/base'
|
||||
}
|
||||
} as any
|
||||
}, () => {
|
||||
expect(nuxtLink({ to: 'http://nuxtjs.org/app/about', target: '_blank' }).props.href).toBe('http://nuxtjs.org/app/about')
|
||||
expect(nuxtLink({ to: '//nuxtjs.org/app/about', target: '_blank' }).props.href).toBe('//nuxtjs.org/app/about')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('rel', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user