Skip to content

Commit

Permalink
Merge a9c0753 into 1825636
Browse files Browse the repository at this point in the history
  • Loading branch information
Mister-Hope authored Apr 16, 2024
2 parents 1825636 + a9c0753 commit 8389f0c
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 107 deletions.
99 changes: 59 additions & 40 deletions packages/client/src/components/RouteLink.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { h } from 'vue'
import type { FunctionalComponent, HTMLAttributes, VNode } from 'vue'
import { useRouter } from 'vue-router'
import { removeLeadingSlash } from '@vuepress/shared'
import { computed, defineComponent, h } from 'vue'
import type { SlotsType, VNode } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { resolveRoutePath } from '../router/index.js'
import { withBase } from '../utils/index.js'

/**
* Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293
Expand All @@ -23,7 +23,7 @@ const guardEvent = (event: MouseEvent): boolean | void => {
return true
}

export interface RouteLinkProps extends HTMLAttributes {
export interface RouteLinkProps {
/**
* Whether the link is active to have an active class
*
Expand Down Expand Up @@ -53,42 +53,61 @@ export interface RouteLinkProps extends HTMLAttributes {
*
* It's recommended to use `RouteLink` in VuePress.
*/
export const RouteLink: FunctionalComponent<
RouteLinkProps,
Record<never, never>,
{
default: () => string | VNode | (string | VNode)[]
}
> = (
{ active = false, activeClass = 'route-link-active', to, ...attrs },
{ slots },
) => {
const router = useRouter()
const resolvedPath = resolveRoutePath(to)
export const RouteLink = defineComponent({
name: 'RouteLink',

const path =
// only anchor or query
resolvedPath.startsWith('#') || resolvedPath.startsWith('?')
? resolvedPath
: withBase(resolvedPath)
props: {
/**
* The route path to link to
*/
to: {
type: String,
required: true,
},

return h(
'a',
{
...attrs,
class: ['route-link', { [activeClass]: active }],
href: path,
onClick: (event: MouseEvent = {} as MouseEvent) => {
guardEvent(event) ? router.push(to).catch() : Promise.resolve()
},
/**
* Whether the link is active to have an active class
*
* Notice that the active status is not automatically determined according to the current route.
*/
active: Boolean,

/**
* The class to add when the link is active
*/
activeClass: {
type: String,
default: 'route-link-active',
},
slots.default?.(),
)
}
},

RouteLink.displayName = 'RouteLink'
RouteLink.props = {
active: Boolean,
activeClass: String,
to: String,
}
slots: Object as SlotsType<{
default: () => string | VNode | (string | VNode)[]
}>,

setup(props, { slots }) {
const router = useRouter()
const route = useRoute()

const path = computed(() =>
props.to.startsWith('#') || props.to.startsWith('?')
? props.to
: `${__VUEPRESS_BASE__}${removeLeadingSlash(resolveRoutePath(props.to, route.path))}`,
)

return () =>
h(
'a',
{
class: ['route-link', { [props.activeClass]: props.active }],
href: path.value,
onClick: (event: MouseEvent = {} as MouseEvent) => {
if (guardEvent(event)) {
router.push(props.to).catch()
}
},
},
slots.default?.(),
)
},
})
3 changes: 2 additions & 1 deletion packages/client/src/router/resolveRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export interface ResolvedRoute<T extends RouteMeta = RouteMeta>
*/
export const resolveRoute = <T extends RouteMeta = RouteMeta>(
path: string,
current?: string,
): ResolvedRoute<T> => {
const routePath = resolveRoutePath(path)
const routePath = resolveRoutePath(path, current)
const route = routes.value[routePath] ?? {
...routes.value['/404.html'],
notFound: true,
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/router/resolveRoutePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { redirects, routes } from '../internal/routes.js'
/**
* Resolve route path with given raw path
*/
export const resolveRoutePath = (path: string): string => {
export const resolveRoutePath = (path: string, current?: string): string => {
// normalized path
const normalizedPath = normalizeRoutePath(path)
const normalizedPath = normalizeRoutePath(path, current)
if (routes.value[normalizedPath]) return normalizedPath

// encoded path
Expand Down
33 changes: 23 additions & 10 deletions packages/shared/src/utils/normalizeRoutePath.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
/**
* Normalize the given path to the final route path
*/
export const normalizeRoutePath = (path: string): string => {
// split pathname and query/hash
const [pathname, ...queryAndHash] = path.split(/(\?|#)/)
const FAKE_HOST = 'http://.'

export const inferRoutePath = (path: string): string => {
// if the pathname is empty or ends with `/`, return as is
if (!pathname || pathname.endsWith('/')) return path
if (!path || path.endsWith('/')) return path

// convert README.md to index.html
let routePath = pathname.replace(/(^|\/)README.md$/i, '$1index.html')
let routePath = path.replace(/(^|\/)README.md$/i, '$1index.html')

// convert /foo/bar.md to /foo/bar.html
if (routePath.endsWith('.md')) {
Expand All @@ -25,6 +21,23 @@ export const normalizeRoutePath = (path: string): string => {
routePath = routePath.substring(0, routePath.length - 10)
}

// add query and hash back
return routePath + queryAndHash.join('')
return routePath
}

/**
* Normalize the given path to the final route path
*/
export const normalizeRoutePath = (path: string, current?: string): string => {
if (!path.startsWith('/') && current) {
// the relative path should be resolved against the current path
const loc = current.slice(0, current.lastIndexOf('/'))

const { pathname, search, hash } = new URL(`${loc}/${path}`, FAKE_HOST)

return inferRoutePath(pathname) + search + hash
}

const [pathname, ...queryAndHash] = path.split(/(\?|#)/)

return inferRoutePath(pathname) + queryAndHash.join('')
}
Loading

0 comments on commit 8389f0c

Please sign in to comment.