Skip to content

Commit

Permalink
feat: backport navigateTo updates from v3 (#666)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreyYolkin authored Dec 15, 2022
1 parent 33560f2 commit 695ddd7
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 11 deletions.
43 changes: 33 additions & 10 deletions packages/bridge/src/runtime/composables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { getCurrentInstance, onBeforeUnmount, isRef, watch, reactive, toRef, isR
import type { CombinedVueInstance } from 'vue/types/vue'
import type { MetaInfo } from 'vue-meta'
import type VueRouter from 'vue-router'
import type { Location, RawLocation, Route } from 'vue-router'
import type { Location, RawLocation, Route, NavigationFailure } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/schema'
import { sendRedirect } from 'h3'
import { defu } from 'defu'
import { useRouter as useVueRouter, useRoute as useVueRoute } from 'vue-router/composables'
import { joinURL } from 'ufo'
import { hasProtocol, joinURL, parseURL } from 'ufo'
import { useNuxtApp } from './app'

export { useLazyAsyncData, refreshNuxtData } from './asyncData'
Expand Down Expand Up @@ -189,21 +189,44 @@ const isProcessingMiddleware = () => {

export interface NavigateToOptions {
replace?: boolean
redirectCode?: number,
external?: boolean
}

export const navigateTo = (to: RawLocation, options: NavigateToOptions = {}): Promise<RawLocation | Route | void> | RawLocation | Route => {
if (isProcessingMiddleware()) {
export const navigateTo = (to: RawLocation | undefined | null, options?: NavigateToOptions): Promise<void | Route | NavigationFailure> | RawLocation | Route => {
if (!to) {
to = '/'
}
const toPath = typeof to === 'string' ? to : (to.path || '/')
const isExternal = hasProtocol(toPath, true)
if (isExternal && !options?.external) {
throw new Error('Navigating to external URL is not allowed by default. Use `nagivateTo (url, { external: true })`.')
}
if (isExternal && parseURL(toPath).protocol === 'script:') {
throw new Error('Cannot navigate to an URL with script protocol.')
}
// Early redirect on client-side
if (process.client && !isExternal && isProcessingMiddleware()) {
return to
}
const router = useRouter()
if (process.server && useNuxtApp().ssrContext) {
// Server-side redirection using h3 res from ssrContext
const event = useNuxtApp().ssrContext?.event
const redirectLocation = joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/')
return sendRedirect(event, redirectLocation)
if (process.server) {
const nuxtApp = useNuxtApp()
if (nuxtApp.ssrContext && nuxtApp.ssrContext.event) {
const redirectLocation = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/')
return nuxtApp.callHook('app:redirected').then(() => sendRedirect(nuxtApp.ssrContext!.event, redirectLocation, options?.redirectCode || 302))
}
}
// Client-side redirection using vue-router
return options.replace ? router.replace(to) : router.push(to)
if (isExternal) {
if (options?.replace) {
location.replace(toPath)
} else {
location.href = toPath
}
return Promise.resolve()
}
return options?.replace ? router.replace(to) : router.push(to)
}

/** This will abort navigation within a Nuxt route middleware handler. */
Expand Down
7 changes: 7 additions & 0 deletions playground/pages/navigate-to-external.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<template>
<div>You should not see me</div>
</template>

<script setup>
navigateTo('https://example.com/', { external: true })
</script>
2 changes: 1 addition & 1 deletion playground/pages/navigate-to.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
</template>

<script setup>
navigateTo('/', { replace: true })
navigateTo('/', { replace: true, redirectCode: 301 })
</script>
13 changes: 13 additions & 0 deletions test/bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ describe('navigate', () => {
expect(headers.get('location')).toEqual('/')
await expectNoClientErrors('/navigate-to/')
})
it('should redirect to index with navigateTo and 301 code', async () => {
const res = await fetch('/navigate-to/', { redirect: 'manual' })
expect(res.status).toBe(301)
await expectNoClientErrors('/navigate-to/')
})
})

describe('navigate external', () => {
it('should redirect to example.com', async () => {
const { headers } = await fetch('/navigate-to-external/', { redirect: 'manual' })

expect(headers.get('location')).toEqual('https://example.com/')
})
})

describe('legacy capi', () => {
Expand Down

0 comments on commit 695ddd7

Please sign in to comment.