diff --git a/packages/shared/src/utils/normalizeRoutePath.ts b/packages/shared/src/utils/normalizeRoutePath.ts index fd2debb2fc..c61f669b0f 100644 --- a/packages/shared/src/utils/normalizeRoutePath.ts +++ b/packages/shared/src/utils/normalizeRoutePath.ts @@ -2,27 +2,31 @@ * Normalize the given path to the final route path */ export const normalizeRoutePath = (path: string): string => { - if (!path || path.endsWith('/')) { - return path - } + // split pathname and query/hash + const [pathname, ...rest] = path.split(/(\?|#)/) + + // if the pathname is empty or ends with `/`, return as is + if (!pathname || pathname.endsWith('/')) return path + + // join query and hash + const queryAndHash = rest.length > 0 ? rest.join('') : '' // convert README.md to index.html - let routePath = path.replace(/(^|\/)README.md$/i, '$1index.html') + let routePath = pathname.replace(/(^|\/)README.md$/i, '$1index.html') // convert /foo/bar.md to /foo/bar.html if (routePath.endsWith('.md')) { routePath = routePath.substring(0, routePath.length - 3) + '.html' } - // convert /foo/bar to /foo/bar.html - if (!routePath.endsWith('.html')) { + else if (!routePath.endsWith('.html')) { routePath = routePath + '.html' } // convert /foo/index.html to /foo/ if (routePath.endsWith('/index.html')) { - return routePath.substring(0, routePath.length - 10) + return routePath.substring(0, routePath.length - 10) + queryAndHash } - return routePath + return routePath + queryAndHash } diff --git a/packages/shared/tests/normalizeRoutePath.spec.ts b/packages/shared/tests/normalizeRoutePath.spec.ts index 7790884780..facc9e0e5f 100644 --- a/packages/shared/tests/normalizeRoutePath.spec.ts +++ b/packages/shared/tests/normalizeRoutePath.spec.ts @@ -1,4 +1,4 @@ -import { expect, it } from 'vitest' +import { describe, expect, it } from 'vitest' import { normalizeRoutePath } from '../src/index.js' const testCases = [ @@ -49,8 +49,39 @@ const testCases = [ ['/foo/.md', '/foo/.html'], ] -testCases.forEach(([path, expected]) => - it(`should normalize "${path}" to "${expected}"`, () => { - expect(normalizeRoutePath(path)).toBe(expected) - }), -) +describe('should normalize clean paths correctly', () => + testCases.forEach(([path, expected]) => + it(`"${path}" -> "${expected}"`, () => { + expect(normalizeRoutePath(path)).toBe(expected) + }), + )) + +describe('should normalize paths with query correctly', () => + testCases + .map(([path, expected]) => [`${path}?foo=bar`, `${expected}?foo=bar`]) + .forEach(([path, expected]) => + it(`"${path}" -> "${expected}"`, () => { + expect(normalizeRoutePath(path)).toBe(expected) + }), + )) + +describe('should normalize paths with hash correctly', () => + testCases + .map(([path, expected]) => [`${path}#foobar`, `${expected}#foobar`]) + .forEach(([path, expected]) => + it(`"${path}" -> "${expected}"`, () => { + expect(normalizeRoutePath(path)).toBe(expected) + }), + )) + +describe('should normalize paths with query and hash correctly', () => + testCases + .map(([path, expected]) => [ + `${path}?foo=1&bar=2#foobar`, + `${expected}?foo=1&bar=2#foobar`, + ]) + .forEach(([path, expected]) => + it(`"${path}" -> "${expected}"`, () => { + expect(normalizeRoutePath(path)).toBe(expected) + }), + ))