From f6ed49f0ac3c56953350c5f89000e2586d6f465b Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 16:12:45 +0800 Subject: [PATCH 1/9] fix: router beforeResolve `fullPath` error --- packages/client/src/router/createVueRouter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/src/router/createVueRouter.ts b/packages/client/src/router/createVueRouter.ts index a9dd605096..32b211749c 100644 --- a/packages/client/src/router/createVueRouter.ts +++ b/packages/client/src/router/createVueRouter.ts @@ -40,9 +40,10 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const route = resolveRoute(to.fullPath) + const fullPath = to.fullPath.split('#')[0] + const route = resolveRoute(fullPath) - if (route.path !== to.fullPath) { + if (route.path !== fullPath) { return route.path } const pageChunk = await route.loader() From de2fa24bf587c2bf43a32a9b25c383e06c36c3b6 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 16:31:45 +0800 Subject: [PATCH 2/9] fix: route resolve error --- packages/client/src/router/createVueRouter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/router/createVueRouter.ts b/packages/client/src/router/createVueRouter.ts index 32b211749c..475cca99ba 100644 --- a/packages/client/src/router/createVueRouter.ts +++ b/packages/client/src/router/createVueRouter.ts @@ -40,7 +40,8 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const fullPath = to.fullPath.split('#')[0] + console.log(to) + const fullPath = to.fullPath.split(/#|\?/)[0] const route = resolveRoute(fullPath) if (route.path !== fullPath) { From da2436d423352655917b4d67056afdda78a10284 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 16:33:51 +0800 Subject: [PATCH 3/9] chore: tweak --- packages/client/src/router/createVueRouter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/router/createVueRouter.ts b/packages/client/src/router/createVueRouter.ts index 475cca99ba..69f6b810cd 100644 --- a/packages/client/src/router/createVueRouter.ts +++ b/packages/client/src/router/createVueRouter.ts @@ -40,7 +40,6 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - console.log(to) const fullPath = to.fullPath.split(/#|\?/)[0] const route = resolveRoute(fullPath) From 2b77a53b691de930164e72b860e83b6f7d78cf78 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 16:47:07 +0800 Subject: [PATCH 4/9] chore: tweak --- packages/client/src/router/createVueRouter.ts | 6 ++---- packages/client/src/router/resolveRoute.ts | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/client/src/router/createVueRouter.ts b/packages/client/src/router/createVueRouter.ts index 69f6b810cd..6cbb8fa8e7 100644 --- a/packages/client/src/router/createVueRouter.ts +++ b/packages/client/src/router/createVueRouter.ts @@ -40,10 +40,8 @@ export const createVueRouter = (): Router => { // and save page data to route meta router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { - const fullPath = to.fullPath.split(/#|\?/)[0] - const route = resolveRoute(fullPath) - - if (route.path !== fullPath) { + const route = resolveRoute(to.fullPath) + if (route.path !== to.fullPath) { return route.path } const pageChunk = await route.loader() diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index e4345c9a9b..bd7a6e8b06 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -16,7 +16,8 @@ export const resolveRoute = ( currentPath?: string, ): ResolvedRoute => { const routePath = resolveRoutePath(path, currentPath) - const route = routes.value[routePath] ?? { + const pathname = routePath.split(/#|\?/)[0] + const route = routes.value[pathname] ?? { ...routes.value['/404.html'], notFound: true, } From 1995208b6b95a18962176796ce507d3ca5cad388 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 17:01:46 +0800 Subject: [PATCH 5/9] chore: tweak --- packages/client/src/router/createVueRouter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/router/createVueRouter.ts b/packages/client/src/router/createVueRouter.ts index 6cbb8fa8e7..a9dd605096 100644 --- a/packages/client/src/router/createVueRouter.ts +++ b/packages/client/src/router/createVueRouter.ts @@ -41,6 +41,7 @@ export const createVueRouter = (): Router => { router.beforeResolve(async (to, from): Promise => { if (to.path !== from.path || from === START_LOCATION) { const route = resolveRoute(to.fullPath) + if (route.path !== to.fullPath) { return route.path } From 6a9c40fdaa1f46175c89fad72d1a26140699bb27 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 18:49:00 +0800 Subject: [PATCH 6/9] chore: tweak --- packages/client/src/router/resolveRoute.ts | 3 ++- .../client/src/router/resolveRoutePath.ts | 14 ++++++------- packages/shared/src/utils/index.ts | 1 + .../shared/src/utils/resolveRoutePathname.ts | 8 +++++++ .../shared/tests/resolveRoutePathname.spec.ts | 21 +++++++++++++++++++ 5 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 packages/shared/src/utils/resolveRoutePathname.ts create mode 100644 packages/shared/tests/resolveRoutePathname.spec.ts diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index bd7a6e8b06..2a35c223c8 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -1,3 +1,4 @@ +import { resolveRoutePathname } from '@vuepress/shared' import { routes } from '../internal/routes.js' import type { Route, RouteMeta } from '../types/index.js' import { resolveRoutePath } from './resolveRoutePath.js' @@ -16,7 +17,7 @@ export const resolveRoute = ( currentPath?: string, ): ResolvedRoute => { const routePath = resolveRoutePath(path, currentPath) - const pathname = routePath.split(/#|\?/)[0] + const pathname = resolveRoutePathname(routePath) const route = routes.value[pathname] ?? { ...routes.value['/404.html'], notFound: true, diff --git a/packages/client/src/router/resolveRoutePath.ts b/packages/client/src/router/resolveRoutePath.ts index f5aece8f9b..db1bd15f7b 100644 --- a/packages/client/src/router/resolveRoutePath.ts +++ b/packages/client/src/router/resolveRoutePath.ts @@ -1,4 +1,4 @@ -import { normalizeRoutePath } from '@vuepress/shared' +import { normalizeRoutePath, resolveRoutePathname } from '@vuepress/shared' import { redirects, routes } from '../internal/routes.js' /** @@ -10,16 +10,16 @@ export const resolveRoutePath = ( ): string => { // normalized path const normalizedPath = normalizeRoutePath(path, currentPath) - if (routes.value[normalizedPath]) return normalizedPath - + const normalizedPathname = resolveRoutePathname(normalizedPath) + if (routes.value[normalizedPathname]) return normalizedPath // encoded path - const encodedPath = encodeURI(normalizedPath) - if (routes.value[encodedPath]) return encodedPath + const encodedPathname = encodeURI(normalizedPathname) + if (routes.value[encodedPathname]) return encodeURI(normalizedPath) // redirected path or fallback to the normalized path return ( - redirects.value[normalizedPath] || - redirects.value[encodedPath] || + redirects.value[normalizedPathname] || + redirects.value[encodedPathname] || normalizedPath ) } diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index 2d329ba2c3..cbe29c4f2c 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -15,4 +15,5 @@ export * from './removeLeadingSlash.js' export * from './resolveHeadIdentifier.js' export * from './resolveLocalePath.js' export * from './resolveRoutePathFromUrl.js' +export * from './resolveRoutePathname.js' export * from './typeGuards.js' diff --git a/packages/shared/src/utils/resolveRoutePathname.ts b/packages/shared/src/utils/resolveRoutePathname.ts new file mode 100644 index 0000000000..6dd10d2803 --- /dev/null +++ b/packages/shared/src/utils/resolveRoutePathname.ts @@ -0,0 +1,8 @@ +const SPLIT_CHAR_RE = /#|\?/ + +/** + * Resolve the route pathname + */ +export const resolveRoutePathname = (path: string): string => { + return path.split(SPLIT_CHAR_RE)[0] +} diff --git a/packages/shared/tests/resolveRoutePathname.spec.ts b/packages/shared/tests/resolveRoutePathname.spec.ts new file mode 100644 index 0000000000..9f1f0ef848 --- /dev/null +++ b/packages/shared/tests/resolveRoutePathname.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { resolveRoutePathname } from '../src/index.js' + +const testCases = [ + ['/a/b/c/', '/a/b/c/'], + ['/a/b/c/?a=1', '/a/b/c/'], + ['/a/b/c/#b', '/a/b/c/'], + ['/a/b/c/?a=1#b', '/a/b/c/'], + ['a/index.html', 'a/index.html'], + ['/a/index.html?a=1', '/a/index.html'], + ['/a/index.html#a', '/a/index.html'], + ['/a/index.html?a=1#b', '/a/index.html'], +] + +describe('should resolve route pathname correctly', () => { + testCases.forEach(([source, expected]) => { + it(`${source} -> ${expected}`, () => { + expect(resolveRoutePathname(source)).toBe(expected) + }) + }) +}) From 7648075720e07a262cfde559d619d05306cdc537 Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 23:20:23 +0800 Subject: [PATCH 7/9] fix: permalink redirect error --- packages/client/src/router/resolveRoute.ts | 4 ++-- .../client/src/router/resolveRoutePath.ts | 20 +++++++++++------- packages/shared/src/utils/index.ts | 2 +- .../shared/src/utils/normalizeRoutePath.ts | 5 +++-- .../shared/src/utils/resolveRouteFullPath.ts | 10 +++++++++ .../shared/src/utils/resolveRoutePathname.ts | 8 ------- .../shared/tests/resolveRouteFullPath.spec.ts | 21 +++++++++++++++++++ .../shared/tests/resolveRoutePathname.spec.ts | 21 ------------------- 8 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 packages/shared/src/utils/resolveRouteFullPath.ts delete mode 100644 packages/shared/src/utils/resolveRoutePathname.ts create mode 100644 packages/shared/tests/resolveRouteFullPath.spec.ts delete mode 100644 packages/shared/tests/resolveRoutePathname.spec.ts diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index 2a35c223c8..b0d9580a25 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -1,4 +1,4 @@ -import { resolveRoutePathname } from '@vuepress/shared' +import { resolveRouteFullPath } from '@vuepress/shared' import { routes } from '../internal/routes.js' import type { Route, RouteMeta } from '../types/index.js' import { resolveRoutePath } from './resolveRoutePath.js' @@ -17,7 +17,7 @@ export const resolveRoute = ( currentPath?: string, ): ResolvedRoute => { const routePath = resolveRoutePath(path, currentPath) - const pathname = resolveRoutePathname(routePath) + const [pathname] = resolveRouteFullPath(routePath) const route = routes.value[pathname] ?? { ...routes.value['/404.html'], notFound: true, diff --git a/packages/client/src/router/resolveRoutePath.ts b/packages/client/src/router/resolveRoutePath.ts index db1bd15f7b..85dfbd1f09 100644 --- a/packages/client/src/router/resolveRoutePath.ts +++ b/packages/client/src/router/resolveRoutePath.ts @@ -1,4 +1,4 @@ -import { normalizeRoutePath, resolveRoutePathname } from '@vuepress/shared' +import { normalizeRoutePath, resolveRouteFullPath } from '@vuepress/shared' import { redirects, routes } from '../internal/routes.js' /** @@ -10,16 +10,20 @@ export const resolveRoutePath = ( ): string => { // normalized path const normalizedPath = normalizeRoutePath(path, currentPath) - const normalizedPathname = resolveRoutePathname(normalizedPath) + const [normalizedPathname, queryAndHash] = + resolveRouteFullPath(normalizedPath) + if (routes.value[normalizedPathname]) return normalizedPath + // encoded path const encodedPathname = encodeURI(normalizedPathname) if (routes.value[encodedPathname]) return encodeURI(normalizedPath) - // redirected path or fallback to the normalized path - return ( - redirects.value[normalizedPathname] || - redirects.value[encodedPathname] || - normalizedPath - ) + // redirected path + const redirectPath = + redirects.value[normalizedPathname] || redirects.value[encodedPathname] + if (redirectPath) return redirectPath + queryAndHash + + // fallback to the normalized path + return normalizedPath } diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index cbe29c4f2c..4af399a350 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -15,5 +15,5 @@ export * from './removeLeadingSlash.js' export * from './resolveHeadIdentifier.js' export * from './resolveLocalePath.js' export * from './resolveRoutePathFromUrl.js' -export * from './resolveRoutePathname.js' +export * from './resolveRouteFullPath.js' export * from './typeGuards.js' diff --git a/packages/shared/src/utils/normalizeRoutePath.ts b/packages/shared/src/utils/normalizeRoutePath.ts index 334fc308c1..cd50bd42cf 100644 --- a/packages/shared/src/utils/normalizeRoutePath.ts +++ b/packages/shared/src/utils/normalizeRoutePath.ts @@ -1,4 +1,5 @@ import { inferRoutePath } from './inferRoutePath.js' +import { resolveRouteFullPath } from './resolveRouteFullPath.js' const FAKE_HOST = 'http://.' @@ -15,7 +16,7 @@ export const normalizeRoutePath = (path: string, current?: string): string => { return inferRoutePath(pathname) + search + hash } - const [pathname, ...queryAndHash] = path.split(/(\?|#)/) + const [pathname, queryAndHash] = resolveRouteFullPath(path) - return inferRoutePath(pathname) + queryAndHash.join('') + return inferRoutePath(pathname) + queryAndHash } diff --git a/packages/shared/src/utils/resolveRouteFullPath.ts b/packages/shared/src/utils/resolveRouteFullPath.ts new file mode 100644 index 0000000000..bc92534677 --- /dev/null +++ b/packages/shared/src/utils/resolveRouteFullPath.ts @@ -0,0 +1,10 @@ +const SPLIT_CHAR_RE = /(#|\?)/ + +/** + * Resolve the route.fullPath to [pathname, queryAndHash] + */ +export const resolveRouteFullPath = (path: string): [string, string] => { + const [pathname, ...queryAndHash] = path.split(SPLIT_CHAR_RE) + + return [pathname, queryAndHash.join('')] +} diff --git a/packages/shared/src/utils/resolveRoutePathname.ts b/packages/shared/src/utils/resolveRoutePathname.ts deleted file mode 100644 index 6dd10d2803..0000000000 --- a/packages/shared/src/utils/resolveRoutePathname.ts +++ /dev/null @@ -1,8 +0,0 @@ -const SPLIT_CHAR_RE = /#|\?/ - -/** - * Resolve the route pathname - */ -export const resolveRoutePathname = (path: string): string => { - return path.split(SPLIT_CHAR_RE)[0] -} diff --git a/packages/shared/tests/resolveRouteFullPath.spec.ts b/packages/shared/tests/resolveRouteFullPath.spec.ts new file mode 100644 index 0000000000..983aafa33e --- /dev/null +++ b/packages/shared/tests/resolveRouteFullPath.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { resolveRouteFullPath } from '../src/index.js' + +const testCases: [string, [string, string]][] = [ + ['/a/b/c/', ['/a/b/c/', '']], + ['/a/b/c/?a=1', ['/a/b/c/', '?a=1']], + ['/a/b/c/#b', ['/a/b/c/', '#b']], + ['/a/b/c/?a=1#b', ['/a/b/c/', '?a=1#b']], + ['a/index.html', ['a/index.html', '']], + ['/a/index.html?a=1', ['/a/index.html', '?a=1']], + ['/a/index.html#a', ['/a/index.html', '#a']], + ['/a/index.html?a=1#b', ['/a/index.html', '?a=1#b']], +] + +describe('should resolve route pathname correctly', () => { + testCases.forEach(([source, expected]) => { + it(`${source} -> ${expected}`, () => { + expect(resolveRouteFullPath(source)).toEqual(expected) + }) + }) +}) diff --git a/packages/shared/tests/resolveRoutePathname.spec.ts b/packages/shared/tests/resolveRoutePathname.spec.ts deleted file mode 100644 index 9f1f0ef848..0000000000 --- a/packages/shared/tests/resolveRoutePathname.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { resolveRoutePathname } from '../src/index.js' - -const testCases = [ - ['/a/b/c/', '/a/b/c/'], - ['/a/b/c/?a=1', '/a/b/c/'], - ['/a/b/c/#b', '/a/b/c/'], - ['/a/b/c/?a=1#b', '/a/b/c/'], - ['a/index.html', 'a/index.html'], - ['/a/index.html?a=1', '/a/index.html'], - ['/a/index.html#a', '/a/index.html'], - ['/a/index.html?a=1#b', '/a/index.html'], -] - -describe('should resolve route pathname correctly', () => { - testCases.forEach(([source, expected]) => { - it(`${source} -> ${expected}`, () => { - expect(resolveRoutePathname(source)).toBe(expected) - }) - }) -}) From 24dd4ac1fa59d6dcfa3b6450dd16a61844304faf Mon Sep 17 00:00:00 2001 From: pengzhanbo Date: Sun, 19 May 2024 23:20:43 +0800 Subject: [PATCH 8/9] test: add e2e test --- e2e/docs/router/resolve-route-query-hash.md | 12 +++++++ .../router/resolve-route-query-hash.spec.ts | 35 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 e2e/docs/router/resolve-route-query-hash.md create mode 100644 e2e/tests/router/resolve-route-query-hash.spec.ts diff --git a/e2e/docs/router/resolve-route-query-hash.md b/e2e/docs/router/resolve-route-query-hash.md new file mode 100644 index 0000000000..a21266220f --- /dev/null +++ b/e2e/docs/router/resolve-route-query-hash.md @@ -0,0 +1,12 @@ +# Resolve Route FullPath + +## Includes Query And Hash + +- Search Query: {{ JSON.stringify(resolveRoute('/?query=1')) }} +- Hash: {{ JSON.stringify(resolveRoute('/#hash')) }} +- Search Query And Hash: {{ JSON.stringify(resolveRoute('/?query=1#hash')) }} +- Permalink And Search Query: {{ JSON.stringify(resolveRoute('/routes/permalinks/ascii-non-ascii.md?query=1')) }} + + diff --git a/e2e/tests/router/resolve-route-query-hash.spec.ts b/e2e/tests/router/resolve-route-query-hash.spec.ts new file mode 100644 index 0000000000..d7554c6425 --- /dev/null +++ b/e2e/tests/router/resolve-route-query-hash.spec.ts @@ -0,0 +1,35 @@ +import { expect, test } from '@playwright/test' + +const testCases = [ + { + path: '/?query=1', + notFound: false, + }, + { + path: '/#hash', + notFound: false, + }, + { + path: '/?query=1#hash', + notFound: false, + }, + { + path: encodeURI('/永久链接-ascii-中文/?query=1'), + notFound: false, + }, +] + +test('should resolve routes when including both the query and hash', async ({ + page, +}) => { + const listItemsLocator = await page + .locator('.e2e-theme-content #includes-query-and-hash + ul > li') + .all() + + for (const [index, li] of listItemsLocator.entries()) { + const textContent = await li.textContent() + const resolvedRoute = JSON.parse(/: (\{.*\})\s*$/.exec(textContent!)![1]) + expect(resolvedRoute.path).toEqual(testCases[index].path) + expect(resolvedRoute.notFound).toEqual(testCases[index].notFound) + } +}) From edd2fc1f91cde796a2fa5f3efdffdf4dc7f406f7 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Mon, 20 May 2024 13:00:10 +0800 Subject: [PATCH 9/9] feat: unify api name and avoid unnecessary recalculation --- packages/client/src/components/RouteLink.ts | 4 +- packages/client/src/router/index.ts | 1 + packages/client/src/router/resolveRoute.ts | 25 ++++++++---- .../client/src/router/resolveRouteFullPath.ts | 14 +++++++ .../client/src/router/resolveRoutePath.ts | 35 ++++++++++------- packages/shared/src/utils/index.ts | 2 +- .../shared/src/utils/normalizeRoutePath.ts | 18 ++++----- .../shared/src/utils/resolveRouteFullPath.ts | 10 ----- .../shared/src/utils/resolveRoutePathInfo.ts | 12 ++++++ .../shared/tests/normalizeRoutePath.spec.ts | 39 ------------------- ...h.spec.ts => resolveRoutePathInfo.spec.ts} | 6 +-- 11 files changed, 78 insertions(+), 88 deletions(-) create mode 100644 packages/client/src/router/resolveRouteFullPath.ts delete mode 100644 packages/shared/src/utils/resolveRouteFullPath.ts create mode 100644 packages/shared/src/utils/resolveRoutePathInfo.ts rename packages/shared/tests/{resolveRouteFullPath.spec.ts => resolveRoutePathInfo.spec.ts} (75%) diff --git a/packages/client/src/components/RouteLink.ts b/packages/client/src/components/RouteLink.ts index 99c4875533..1b1e62b157 100644 --- a/packages/client/src/components/RouteLink.ts +++ b/packages/client/src/components/RouteLink.ts @@ -1,7 +1,7 @@ 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 { resolveRouteFullPath } from '../router/index.js' /** * Forked from https://github.com/vuejs/router/blob/941b2131e80550009e5221d4db9f366b1fea3fd5/packages/router/src/RouterLink.ts#L293 @@ -91,7 +91,7 @@ export const RouteLink = defineComponent({ const path = computed(() => props.to.startsWith('#') || props.to.startsWith('?') ? props.to - : `${__VUEPRESS_BASE__}${resolveRoutePath(props.to, route.path).substring(1)}`, + : `${__VUEPRESS_BASE__}${resolveRouteFullPath(props.to, route.path).substring(1)}`, ) return () => diff --git a/packages/client/src/router/index.ts b/packages/client/src/router/index.ts index dae3d58e2c..206f6f2f2b 100644 --- a/packages/client/src/router/index.ts +++ b/packages/client/src/router/index.ts @@ -2,4 +2,5 @@ export type { Router, RouteLocationNormalizedLoaded } from 'vue-router' export { useRoute, useRouter } from 'vue-router' export * from './resolveRoute.js' +export * from './resolveRouteFullPath.js' export * from './resolveRoutePath.js' diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index b0d9580a25..8cacd9aac5 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -1,4 +1,4 @@ -import { resolveRouteFullPath } from '@vuepress/shared' +import { resolveRoutePathInfo } from '@vuepress/shared' import { routes } from '../internal/routes.js' import type { Route, RouteMeta } from '../types/index.js' import { resolveRoutePath } from './resolveRoutePath.js' @@ -16,16 +16,25 @@ export const resolveRoute = ( path: string, currentPath?: string, ): ResolvedRoute => { - const routePath = resolveRoutePath(path, currentPath) - const [pathname] = resolveRouteFullPath(routePath) - const route = routes.value[pathname] ?? { - ...routes.value['/404.html'], - notFound: true, + // get only the pathname from the path + const [pathname, hashAndQueries] = resolveRoutePathInfo(path) + + // resolve the route path + const routePath = resolveRoutePath(pathname, currentPath) + const routeFullPath = routePath + hashAndQueries + + // the route not found + if (!routes.value[routePath]) { + return { + ...routes.value['/404.html'], + path: routeFullPath, + notFound: true, + } as ResolvedRoute } return { - path: routePath, + ...routes.value[routePath], + path: routeFullPath, notFound: false, - ...route, } as ResolvedRoute } diff --git a/packages/client/src/router/resolveRouteFullPath.ts b/packages/client/src/router/resolveRouteFullPath.ts new file mode 100644 index 0000000000..a07441899a --- /dev/null +++ b/packages/client/src/router/resolveRouteFullPath.ts @@ -0,0 +1,14 @@ +import { resolveRoutePathInfo } from '@vuepress/shared' +import { resolveRoutePath } from './resolveRoutePath.js' + +/** + * Resolve route full path with given raw path + */ +export const resolveRouteFullPath = ( + path: string, + currentPath?: string, +): string => { + const [pathname, hashAndQueries] = resolveRoutePathInfo(path) + + return resolveRoutePath(pathname, currentPath) + hashAndQueries +} diff --git a/packages/client/src/router/resolveRoutePath.ts b/packages/client/src/router/resolveRoutePath.ts index 85dfbd1f09..4f815e4c17 100644 --- a/packages/client/src/router/resolveRoutePath.ts +++ b/packages/client/src/router/resolveRoutePath.ts @@ -1,29 +1,34 @@ -import { normalizeRoutePath, resolveRouteFullPath } from '@vuepress/shared' +import { normalizeRoutePath } from '@vuepress/shared' import { redirects, routes } from '../internal/routes.js' /** * Resolve route path with given raw path */ export const resolveRoutePath = ( - path: string, + pathname: string, currentPath?: string, ): string => { // normalized path - const normalizedPath = normalizeRoutePath(path, currentPath) - const [normalizedPathname, queryAndHash] = - resolveRouteFullPath(normalizedPath) + const normalizedRoutePath = normalizeRoutePath(pathname, currentPath) - if (routes.value[normalizedPathname]) return normalizedPath + // check if the normalized path is in routes + if (routes.value[normalizedRoutePath]) return normalizedRoutePath - // encoded path - const encodedPathname = encodeURI(normalizedPathname) - if (routes.value[encodedPathname]) return encodeURI(normalizedPath) + // check encoded path + const encodedRoutePath = encodeURI(normalizedRoutePath) - // redirected path - const redirectPath = - redirects.value[normalizedPathname] || redirects.value[encodedPathname] - if (redirectPath) return redirectPath + queryAndHash + if (routes.value[encodedRoutePath]) { + return encodedRoutePath + } - // fallback to the normalized path - return normalizedPath + // check redirected path with normalized path and encoded path + const redirectedRoutePath = + redirects.value[normalizedRoutePath] || redirects.value[encodedRoutePath] + + if (redirectedRoutePath) { + return redirectedRoutePath + } + + // default to normalized route path + return normalizedRoutePath } diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index 4af399a350..fb1db555ae 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -15,5 +15,5 @@ export * from './removeLeadingSlash.js' export * from './resolveHeadIdentifier.js' export * from './resolveLocalePath.js' export * from './resolveRoutePathFromUrl.js' -export * from './resolveRouteFullPath.js' +export * from './resolveRoutePathInfo.js' export * from './typeGuards.js' diff --git a/packages/shared/src/utils/normalizeRoutePath.ts b/packages/shared/src/utils/normalizeRoutePath.ts index cd50bd42cf..6c73ef172a 100644 --- a/packages/shared/src/utils/normalizeRoutePath.ts +++ b/packages/shared/src/utils/normalizeRoutePath.ts @@ -1,22 +1,20 @@ import { inferRoutePath } from './inferRoutePath.js' -import { resolveRouteFullPath } from './resolveRouteFullPath.js' const FAKE_HOST = 'http://.' /** - * Normalize the given path to the final route path + * Normalize the given pathname path to the final route path */ -export const normalizeRoutePath = (path: string, current?: string): string => { - if (!path.startsWith('/') && current) { +export const normalizeRoutePath = ( + pathname: string, + current?: string, +): string => { + if (!pathname.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 + return inferRoutePath(new URL(`${loc}/${pathname}`, FAKE_HOST).pathname) } - const [pathname, queryAndHash] = resolveRouteFullPath(path) - - return inferRoutePath(pathname) + queryAndHash + return inferRoutePath(pathname) } diff --git a/packages/shared/src/utils/resolveRouteFullPath.ts b/packages/shared/src/utils/resolveRouteFullPath.ts deleted file mode 100644 index bc92534677..0000000000 --- a/packages/shared/src/utils/resolveRouteFullPath.ts +++ /dev/null @@ -1,10 +0,0 @@ -const SPLIT_CHAR_RE = /(#|\?)/ - -/** - * Resolve the route.fullPath to [pathname, queryAndHash] - */ -export const resolveRouteFullPath = (path: string): [string, string] => { - const [pathname, ...queryAndHash] = path.split(SPLIT_CHAR_RE) - - return [pathname, queryAndHash.join('')] -} diff --git a/packages/shared/src/utils/resolveRoutePathInfo.ts b/packages/shared/src/utils/resolveRoutePathInfo.ts new file mode 100644 index 0000000000..65b0b68bad --- /dev/null +++ b/packages/shared/src/utils/resolveRoutePathInfo.ts @@ -0,0 +1,12 @@ +const SPLIT_CHAR_REGEXP = /(#|\?)/ + +/** + * Extract pathname / hash and queries from a full route path + */ +export const resolveRoutePathInfo = ( + path: string, +): [pathname: string, hashAndQueries: string] => { + const [pathname, ...hashAndQueries] = path.split(SPLIT_CHAR_REGEXP) + + return [pathname, hashAndQueries.join('')] +} diff --git a/packages/shared/tests/normalizeRoutePath.spec.ts b/packages/shared/tests/normalizeRoutePath.spec.ts index 6543ab24e2..7644945e14 100644 --- a/packages/shared/tests/normalizeRoutePath.spec.ts +++ b/packages/shared/tests/normalizeRoutePath.spec.ts @@ -204,42 +204,3 @@ describe('should normalize clean paths correctly', () => { }), ) }) - -describe('should normalize paths with query correctly', () => { - testCases - .map(([[path, current], expected]) => [ - [`${path}?foo=bar`, current], - `${expected}?foo=bar`, - ]) - .forEach(([[path, current], expected]) => - it(`${current ? `"${current}"-` : ''}"${path}" -> "${expected}"`, () => { - expect(normalizeRoutePath(path, current)).toBe(expected) - }), - ) -}) - -describe('should normalize paths with hash correctly', () => { - testCases - .map(([[path, current], expected]) => [ - [`${path}#foobar`, current], - `${expected}#foobar`, - ]) - .map(([[path, current], expected]) => - it(`${current ? `"${current}"-` : ''}"${path}" -> "${expected}"`, () => { - expect(normalizeRoutePath(path, current)).toBe(expected) - }), - ) -}) - -describe('should normalize paths with query and hash correctly', () => { - testCases - .map(([[path, current], expected]) => [ - [`${path}?foo=1&bar=2#foobar`, current], - `${expected}?foo=1&bar=2#foobar`, - ]) - .map(([[path, current], expected]) => - it(`${current ? `"${current}"-` : ''}"${path}" -> "${expected}"`, () => { - expect(normalizeRoutePath(path, current)).toBe(expected) - }), - ) -}) diff --git a/packages/shared/tests/resolveRouteFullPath.spec.ts b/packages/shared/tests/resolveRoutePathInfo.spec.ts similarity index 75% rename from packages/shared/tests/resolveRouteFullPath.spec.ts rename to packages/shared/tests/resolveRoutePathInfo.spec.ts index 983aafa33e..3c704198ba 100644 --- a/packages/shared/tests/resolveRouteFullPath.spec.ts +++ b/packages/shared/tests/resolveRoutePathInfo.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { resolveRouteFullPath } from '../src/index.js' +import { resolveRoutePathInfo } from '../src/index.js' const testCases: [string, [string, string]][] = [ ['/a/b/c/', ['/a/b/c/', '']], @@ -12,10 +12,10 @@ const testCases: [string, [string, string]][] = [ ['/a/index.html?a=1#b', ['/a/index.html', '?a=1#b']], ] -describe('should resolve route pathname correctly', () => { +describe('should resolve route path info correctly', () => { testCases.forEach(([source, expected]) => { it(`${source} -> ${expected}`, () => { - expect(resolveRouteFullPath(source)).toEqual(expected) + expect(resolveRoutePathInfo(source)).toEqual(expected) }) }) })