From 8a0c158aee20268a7efeb55e4ccedf948ed1e4f5 Mon Sep 17 00:00:00 2001 From: Mister-Hope Date: Wed, 22 May 2024 15:57:46 +0800 Subject: [PATCH] feat: support clean url and improve route resolve --- .eslintrc.cjs | 6 + .../src/plugins/vuepressMainPlugin.ts | 1 + .../src/config/handlePluginDefine.ts | 1 + .../cli/src/commands/dev/watchPageFiles.ts | 2 +- packages/client/src/router/index.ts | 1 + packages/client/src/router/resolveRoute.ts | 16 +- .../client/src/router/resolveRouteFullPath.ts | 4 +- packages/client/src/router/resolveRouteKey.ts | 34 ++ .../client/src/router/resolveRoutePath.ts | 29 +- packages/client/types.d.ts | 1 + packages/core/src/app/resolveAppOptions.ts | 19 +- packages/core/src/app/resolveAppPages.ts | 6 +- packages/core/src/page/resolvePagePath.ts | 12 +- .../core/src/page/resolvePagePermalink.ts | 2 +- packages/core/src/types/app/options.ts | 25 +- .../core/tests/app/resolveAppOptions.spec.ts | 7 +- .../core/tests/app/resolveAppPages.spec.ts | 12 +- .../core/tests/page/resolvePagePath.spec.ts | 4 +- .../src/plugins/linksPlugin/linksPlugin.ts | 21 +- packages/shared/src/utils/routes/index.ts | 3 +- .../shared/src/utils/routes/inferRoutePath.ts | 18 +- ...lveRoutePathInfo.ts => resolvePathInfo.ts} | 2 +- .../utils/routes/resolveRoutePathWithExt.ts | 2 + .../tests/routes/inferRoutePath.spec.ts | 42 +-- .../tests/routes/normalizeRoutePath.spec.ts | 318 +++++++++--------- ...thInfo.spec.ts => resolvePathInfo.spec.ts} | 6 +- 26 files changed, 336 insertions(+), 258 deletions(-) create mode 100644 packages/client/src/router/resolveRouteKey.ts rename packages/shared/src/utils/routes/{resolveRoutePathInfo.ts => resolvePathInfo.ts} (88%) create mode 100644 packages/shared/src/utils/routes/resolveRoutePathWithExt.ts rename packages/shared/tests/routes/{resolveRoutePathInfo.spec.ts => resolvePathInfo.spec.ts} (75%) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7f763acda8..44c2c7dc3a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,6 +1,12 @@ module.exports = { root: true, extends: 'vuepress', + + // FIXME: This should be added to `eslint-config-vuepress` + globals: { + __VUEPRESS_CLEAN_URL__: 'readonly', + }, + overrides: [ { files: ['*.ts', '*.vue', '*.cts'], diff --git a/packages/bundler-vite/src/plugins/vuepressMainPlugin.ts b/packages/bundler-vite/src/plugins/vuepressMainPlugin.ts index 05a53d3b0d..9c73ea36a6 100644 --- a/packages/bundler-vite/src/plugins/vuepressMainPlugin.ts +++ b/packages/bundler-vite/src/plugins/vuepressMainPlugin.ts @@ -205,6 +205,7 @@ const resolveDefine = async ({ const define: UserConfig['define'] = { __VUEPRESS_VERSION__: JSON.stringify(app.version), __VUEPRESS_BASE__: JSON.stringify(app.options.base), + __VUEPRESS_CLEAN_URL__: JSON.stringify(app.options.route.cleanUrl), __VUEPRESS_DEV__: JSON.stringify(!isBuild), __VUEPRESS_SSR__: JSON.stringify(isServer), // @see http://link.vuejs.org/feature-flags diff --git a/packages/bundler-webpack/src/config/handlePluginDefine.ts b/packages/bundler-webpack/src/config/handlePluginDefine.ts index d118844d48..7681c70f5a 100644 --- a/packages/bundler-webpack/src/config/handlePluginDefine.ts +++ b/packages/bundler-webpack/src/config/handlePluginDefine.ts @@ -21,6 +21,7 @@ export const handlePluginDefine = async ({ { __VUEPRESS_VERSION__: JSON.stringify(app.version), __VUEPRESS_BASE__: JSON.stringify(app.options.base), + __VUEPRESS_CLEAN_URL__: JSON.stringify(app.options.route.cleanUrl), __VUEPRESS_DEV__: JSON.stringify(!isBuild), __VUEPRESS_SSR__: JSON.stringify(isServer), // @see http://link.vuejs.org/feature-flags diff --git a/packages/cli/src/commands/dev/watchPageFiles.ts b/packages/cli/src/commands/dev/watchPageFiles.ts index 48dcee2060..10c1bf7ad7 100644 --- a/packages/cli/src/commands/dev/watchPageFiles.ts +++ b/packages/cli/src/commands/dev/watchPageFiles.ts @@ -41,7 +41,7 @@ export const watchPageFiles = (app: App): FSWatcher[] => { app.pages.forEach((page) => addDeps(page)) // watch page files - const pagesWatcher = chokidar.watch(app.options.pagePatterns, { + const pagesWatcher = chokidar.watch(app.options.route.pagePatterns, { cwd: app.dir.source(), ignoreInitial: true, }) diff --git a/packages/client/src/router/index.ts b/packages/client/src/router/index.ts index 206f6f2f2b..b7801c6fd7 100644 --- a/packages/client/src/router/index.ts +++ b/packages/client/src/router/index.ts @@ -3,4 +3,5 @@ export { useRoute, useRouter } from 'vue-router' export * from './resolveRoute.js' export * from './resolveRouteFullPath.js' +export * from './resolveRouteKey.js' export * from './resolveRoutePath.js' diff --git a/packages/client/src/router/resolveRoute.ts b/packages/client/src/router/resolveRoute.ts index 8cacd9aac5..8413b5bd38 100644 --- a/packages/client/src/router/resolveRoute.ts +++ b/packages/client/src/router/resolveRoute.ts @@ -1,7 +1,7 @@ -import { resolveRoutePathInfo } from '@vuepress/shared' +import { resolvePathInfo, resolveRoutePathWithExt } from '@vuepress/shared' import { routes } from '../internal/routes.js' import type { Route, RouteMeta } from '../types/index.js' -import { resolveRoutePath } from './resolveRoutePath.js' +import { resolveRouteKey } from './resolveRouteKey.js' export interface ResolvedRoute extends Route { @@ -17,14 +17,16 @@ export const resolveRoute = ( currentPath?: string, ): ResolvedRoute => { // get only the pathname from the path - const [pathname, hashAndQueries] = resolveRoutePathInfo(path) + const [pathname, hashAndQueries] = resolvePathInfo(path) // resolve the route path - const routePath = resolveRoutePath(pathname, currentPath) - const routeFullPath = routePath + hashAndQueries + const routeKey = resolveRouteKey(pathname, currentPath) + const routeFullPath = __VUEPRESS_CLEAN_URL__ + ? routeKey + : resolveRoutePathWithExt(routeKey) + hashAndQueries // the route not found - if (!routes.value[routePath]) { + if (!routes.value[routeKey]) { return { ...routes.value['/404.html'], path: routeFullPath, @@ -33,7 +35,7 @@ export const resolveRoute = ( } return { - ...routes.value[routePath], + ...routes.value[routeKey], path: routeFullPath, notFound: false, } as ResolvedRoute diff --git a/packages/client/src/router/resolveRouteFullPath.ts b/packages/client/src/router/resolveRouteFullPath.ts index a07441899a..a1ab501de6 100644 --- a/packages/client/src/router/resolveRouteFullPath.ts +++ b/packages/client/src/router/resolveRouteFullPath.ts @@ -1,4 +1,4 @@ -import { resolveRoutePathInfo } from '@vuepress/shared' +import { resolvePathInfo } from '@vuepress/shared' import { resolveRoutePath } from './resolveRoutePath.js' /** @@ -8,7 +8,7 @@ export const resolveRouteFullPath = ( path: string, currentPath?: string, ): string => { - const [pathname, hashAndQueries] = resolveRoutePathInfo(path) + const [pathname, hashAndQueries] = resolvePathInfo(path) return resolveRoutePath(pathname, currentPath) + hashAndQueries } diff --git a/packages/client/src/router/resolveRouteKey.ts b/packages/client/src/router/resolveRouteKey.ts new file mode 100644 index 0000000000..8c2e43d371 --- /dev/null +++ b/packages/client/src/router/resolveRouteKey.ts @@ -0,0 +1,34 @@ +import { normalizeRoutePath } from '@vuepress/shared' +import { redirects, routes } from '../internal/routes.js' + +/** + * Resolve route path with given raw path + */ +export const resolveRouteKey = ( + pathname: string, + currentPath?: string, +): string => { + // normalized path + const routePath = normalizeRoutePath(pathname, currentPath) + + // check if the normalized path is in routes + if (routes.value[routePath]) return routePath + + // check encoded path + const encodedRoutePath = encodeURI(routePath) + + if (routes.value[encodedRoutePath]) { + return encodedRoutePath + } + + // check redirected path with normalized path and encoded path + const redirectedRoutePath = + redirects.value[routePath] || redirects.value[encodedRoutePath] + + if (redirectedRoutePath) { + return redirectedRoutePath + } + + // default to normalized route path + return routePath +} diff --git a/packages/client/src/router/resolveRoutePath.ts b/packages/client/src/router/resolveRoutePath.ts index 4f815e4c17..964b7d8399 100644 --- a/packages/client/src/router/resolveRoutePath.ts +++ b/packages/client/src/router/resolveRoutePath.ts @@ -1,5 +1,5 @@ -import { normalizeRoutePath } from '@vuepress/shared' -import { redirects, routes } from '../internal/routes.js' +import { resolveRoutePathWithExt } from '@vuepress/shared' +import { resolveRouteKey } from './resolveRouteKey.js' /** * Resolve route path with given raw path @@ -8,27 +8,8 @@ export const resolveRoutePath = ( pathname: string, currentPath?: string, ): string => { - // normalized path - const normalizedRoutePath = normalizeRoutePath(pathname, currentPath) + // clean route path format used as key in routes + const routeKey = resolveRouteKey(pathname, currentPath) - // check if the normalized path is in routes - if (routes.value[normalizedRoutePath]) return normalizedRoutePath - - // check encoded path - const encodedRoutePath = encodeURI(normalizedRoutePath) - - if (routes.value[encodedRoutePath]) { - return encodedRoutePath - } - - // 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 + return __VUEPRESS_CLEAN_URL__ ? routeKey : resolveRoutePathWithExt(routeKey) } diff --git a/packages/client/types.d.ts b/packages/client/types.d.ts index 2d55050eb3..fdaa55b854 100644 --- a/packages/client/types.d.ts +++ b/packages/client/types.d.ts @@ -1,6 +1,7 @@ declare const __VUEPRESS_VERSION__: string declare const __VUEPRESS_BASE__: string declare const __VUEPRESS_DEV__: boolean +declare const __VUEPRESS_CLEAN_URL__: boolean declare const __VUEPRESS_SSR__: boolean declare const __VUE_HMR_RUNTIME__: Record declare const __VUE_OPTIONS_API__: boolean diff --git a/packages/core/src/app/resolveAppOptions.ts b/packages/core/src/app/resolveAppOptions.ts index d851c3d969..04538847b1 100644 --- a/packages/core/src/app/resolveAppOptions.ts +++ b/packages/core/src/app/resolveAppOptions.ts @@ -38,8 +38,16 @@ export const resolveAppOptions = ({ bundler, debug = false, markdown = {}, - pagePatterns = ['**/*.md', '!.vuepress', '!node_modules'], - permalinkPattern = null, + pagePatterns: _pagePatterns, + permalinkPattern: _permalinkPattern, + route: { + cleanUrl = false, + pagePatterns = ['**/*.md', '!.vuepress', '!node_modules'], + permalinkPattern = null, + } = { + pagePatterns: _pagePatterns, + permalinkPattern: _permalinkPattern, + }, plugins = [], theme, }: AppConfig): AppOptions => ({ @@ -65,8 +73,11 @@ export const resolveAppOptions = ({ bundler, debug, markdown, - pagePatterns, - permalinkPattern, + route: { + cleanUrl, + pagePatterns, + permalinkPattern, + }, plugins, theme, }) diff --git a/packages/core/src/app/resolveAppPages.ts b/packages/core/src/app/resolveAppPages.ts index 2f721fa7fd..69d8f14b15 100644 --- a/packages/core/src/app/resolveAppPages.ts +++ b/packages/core/src/app/resolveAppPages.ts @@ -11,7 +11,7 @@ export const resolveAppPages = async (app: App): Promise => { log('resolveAppPages start') // resolve page absolute file paths according to the page patterns - const pageFilePaths = await globby(app.options.pagePatterns, { + const pageFilePaths = await globby(app.options.route.pagePatterns, { absolute: true, cwd: app.dir.source(), }) @@ -22,7 +22,7 @@ export const resolveAppPages = async (app: App): Promise => { ) // find the 404 page - const notFoundPage = pages.find((page) => page.path === '/404.html') + const notFoundPage = pages.find((page) => page.path === '/404') // if there is a 404 page, set the default layout to NotFound if (notFoundPage) { @@ -32,7 +32,7 @@ export const resolveAppPages = async (app: App): Promise => { else { pages.push( await createPage(app, { - path: '/404.html', + path: '/404', frontmatter: { layout: 'NotFound' }, content: '404 Not Found', }), diff --git a/packages/core/src/page/resolvePagePath.ts b/packages/core/src/page/resolvePagePath.ts index c40e366455..74f0d7aa4f 100644 --- a/packages/core/src/page/resolvePagePath.ts +++ b/packages/core/src/page/resolvePagePath.ts @@ -21,5 +21,15 @@ export const resolvePagePath = ({ ) } - return encodeURI(pagePath.split('/').map(sanitizeFileName).join('/')) + return encodeURI( + pagePath + // clean page path + .replace(/\.html$/, '') + .replace(/\/index$/i, '/') + + // sanitize page path + .split('/') + .map(sanitizeFileName) + .join('/'), + ) } diff --git a/packages/core/src/page/resolvePagePermalink.ts b/packages/core/src/page/resolvePagePermalink.ts index 6a549085f2..14ca26bb13 100644 --- a/packages/core/src/page/resolvePagePermalink.ts +++ b/packages/core/src/page/resolvePagePermalink.ts @@ -34,7 +34,7 @@ export const resolvePagePermalink = ({ } const permalinkPattern = - frontmatter.permalinkPattern || app.options.permalinkPattern + frontmatter.permalinkPattern || app.options.route.permalinkPattern if (!isString(permalinkPattern)) { return null diff --git a/packages/core/src/types/app/options.ts b/packages/core/src/types/app/options.ts index 93271fd4bf..6721a3b930 100644 --- a/packages/core/src/types/app/options.ts +++ b/packages/core/src/types/app/options.ts @@ -5,6 +5,12 @@ import type { Bundler } from '../bundler.js' import type { PluginConfig } from '../plugin.js' import type { Theme } from '../theme.js' +export interface RouteOptions { + cleanUrl?: boolean + pagePatterns?: string[] + permalinkPattern?: string | null +} + /** * Vuepress app common config that shared between dev and build */ @@ -14,11 +20,9 @@ export interface AppConfigCommon extends Partial { temp?: string cache?: string public?: string - debug?: boolean markdown?: MarkdownOptions - pagePatterns?: string[] - permalinkPattern?: string | null + route?: RouteOptions bundler: Bundler theme: Theme plugins?: PluginConfig @@ -95,9 +99,20 @@ export interface AppConfigBuild { /** * Vuepress app config */ -export type AppConfig = AppConfigCommon & AppConfigDev & AppConfigBuild +export type AppConfig = AppConfigCommon & + AppConfigDev & + AppConfigBuild & { + /** @deprecated use route.pagePatterns instead */ + pagePatterns?: string[] + /** @deprecated use route.permalinkPattern instead */ + permalinkPattern?: string | null + } /** * Vuepress app options */ -export type AppOptions = Required +export type AppOptions = Required< + AppConfigCommon & AppConfigDev & AppConfigBuild +> & { + route: Required +} diff --git a/packages/core/tests/app/resolveAppOptions.spec.ts b/packages/core/tests/app/resolveAppOptions.spec.ts index a7f3ff125c..1a269dfac6 100644 --- a/packages/core/tests/app/resolveAppOptions.spec.ts +++ b/packages/core/tests/app/resolveAppOptions.spec.ts @@ -30,8 +30,11 @@ describe('core > app > resolveAppOptions', () => { host: '0.0.0.0', port: 8080, open: false, - pagePatterns: ['**/*.md', '!.vuepress', '!node_modules'], - permalinkPattern: null, + route: { + cleanUrl: false, + pagePatterns: ['**/*.md', '!.vuepress', '!node_modules'], + permalinkPattern: null, + }, templateDev: path.normalize( require.resolve('@vuepress/client/templates/dev.html'), ), diff --git a/packages/core/tests/app/resolveAppPages.spec.ts b/packages/core/tests/app/resolveAppPages.spec.ts index 8d47875eb2..2a737da81d 100644 --- a/packages/core/tests/app/resolveAppPages.spec.ts +++ b/packages/core/tests/app/resolveAppPages.spec.ts @@ -13,9 +13,9 @@ describe('core > app > resolveAppPages', () => { app.markdown = createMarkdown() const pages = await resolveAppPages(app) - const fooPage = pages.find((page) => page.path === '/foo.html') - const barPage = pages.find((page) => page.path === '/bar.html') - const notFoundPage = pages.find((page) => page.path === '/404.html') + const fooPage = pages.find((page) => page.path === '/foo') + const barPage = pages.find((page) => page.path === '/bar') + const notFoundPage = pages.find((page) => page.path === '/404') expect(pages).toHaveLength(3) expect(fooPage?.filePathRelative).toEqual('foo.md') @@ -33,9 +33,9 @@ describe('core > app > resolveAppPages', () => { app.markdown = createMarkdown() const pages = await resolveAppPages(app) - const fooPage = pages.find((page) => page.path === '/foo.html') - const barPage = pages.find((page) => page.path === '/bar.html') - const notFoundPage = pages.find((page) => page.path === '/404.html') + const fooPage = pages.find((page) => page.path === '/foo') + const barPage = pages.find((page) => page.path === '/bar') + const notFoundPage = pages.find((page) => page.path === '/404') expect(pages).toHaveLength(3) expect(fooPage?.filePathRelative).toEqual('foo.md') diff --git a/packages/core/tests/page/resolvePagePath.spec.ts b/packages/core/tests/page/resolvePagePath.spec.ts index 1ca68d2d65..b0b2e1deaa 100644 --- a/packages/core/tests/page/resolvePagePath.spec.ts +++ b/packages/core/tests/page/resolvePagePath.spec.ts @@ -40,7 +40,7 @@ const testCases: [ }, }, ], - '/options.html', + '/options', ], // use permalink [ @@ -92,7 +92,7 @@ const testCases: [ options: {}, }, ], - '/inferred.html', + '/inferred', ], ] diff --git a/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts b/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts index 5fb407656a..d2d0e05b58 100644 --- a/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts +++ b/packages/markdown/src/plugins/linksPlugin/linksPlugin.ts @@ -1,4 +1,8 @@ -import { inferRoutePath, isLinkExternal } from '@vuepress/shared' +import { + inferRoutePath, + isLinkExternal, + resolveRoutePathWithExt, +} from '@vuepress/shared' import type { PluginWithOptions } from 'markdown-it' import type Token from 'markdown-it/lib/token.mjs' import type { MarkdownEnv } from '../../types.js' @@ -114,17 +118,22 @@ export const linksPlugin: PluginWithOptions = ( // normalize markdown file path to route path // we are removing the `base` from absolute path because it should not be // passed to `` or `` - const normalizedPath = inferRoutePath( - absolutePath - ? absolutePath.replace(new RegExp(`^${base}`), '/') - : relativePath, + const normalizedPath = resolveRoutePathWithExt( + inferRoutePath( + absolutePath + ? absolutePath.replace(new RegExp(`^${base}`), '/') + : relativePath, + ), ) // replace the original href link with the normalized path hrefAttr[1] = `${normalizedPath}${rawHashAndQueries}` // set `hasOpenInternalLink` to modify the ending tag hasOpenInternalLink = true } else { - const normalizedPath = inferRoutePath(absolutePath ?? relativePath) + // ext is added here + const normalizedPath = resolveRoutePathWithExt( + inferRoutePath(absolutePath ?? relativePath), + ) // replace the original href link with the normalized path hrefAttr[1] = `${normalizedPath}${rawHashAndQueries}` } diff --git a/packages/shared/src/utils/routes/index.ts b/packages/shared/src/utils/routes/index.ts index 4ec0e91b53..0a6f4a5596 100644 --- a/packages/shared/src/utils/routes/index.ts +++ b/packages/shared/src/utils/routes/index.ts @@ -1,5 +1,6 @@ export * from './inferRoutePath' export * from './normalizeRoutePath.js' +export * from './resolveRoutePathWithExt.js' export * from './resolveLocalePath.js' export * from './resolveRoutePathFromUrl.js' -export * from './resolveRoutePathInfo.js' +export * from './resolvePathInfo.js' diff --git a/packages/shared/src/utils/routes/inferRoutePath.ts b/packages/shared/src/utils/routes/inferRoutePath.ts index 0a1ba2e4aa..863b394852 100644 --- a/packages/shared/src/utils/routes/inferRoutePath.ts +++ b/packages/shared/src/utils/routes/inferRoutePath.ts @@ -6,20 +6,20 @@ export const inferRoutePath = (path: string): string => { if (!path || path.endsWith('/')) return path // convert README.md to index.html - let routePath = path.replace(/(^|\/)README.md$/i, '$1index.html') + let routePath = path.replace(/(^|\/)README.md$/i, '$1index') - // convert /foo/bar.md to /foo/bar.html + // convert /foo/bar.md to /foo/bar if (routePath.endsWith('.md')) { - routePath = routePath.substring(0, routePath.length - 3) + '.html' + routePath = routePath.substring(0, routePath.length - 3) } - // convert /foo/bar to /foo/bar.html - else if (!routePath.endsWith('.html')) { - routePath = routePath + '.html' + // convert /foo/bar.html to /foo/bar + else if (routePath.endsWith('.html')) { + routePath = routePath.substring(0, routePath.length - 5) } - // convert /foo/index.html to /foo/ - if (routePath.endsWith('/index.html')) { - routePath = routePath.substring(0, routePath.length - 10) + // convert /foo/index to /foo/ + if (routePath.endsWith('/index')) { + routePath = routePath.substring(0, routePath.length - 5) } return routePath diff --git a/packages/shared/src/utils/routes/resolveRoutePathInfo.ts b/packages/shared/src/utils/routes/resolvePathInfo.ts similarity index 88% rename from packages/shared/src/utils/routes/resolveRoutePathInfo.ts rename to packages/shared/src/utils/routes/resolvePathInfo.ts index 65b0b68bad..2e0d002e24 100644 --- a/packages/shared/src/utils/routes/resolveRoutePathInfo.ts +++ b/packages/shared/src/utils/routes/resolvePathInfo.ts @@ -3,7 +3,7 @@ const SPLIT_CHAR_REGEXP = /(#|\?)/ /** * Extract pathname / hash and queries from a full route path */ -export const resolveRoutePathInfo = ( +export const resolvePathInfo = ( path: string, ): [pathname: string, hashAndQueries: string] => { const [pathname, ...hashAndQueries] = path.split(SPLIT_CHAR_REGEXP) diff --git a/packages/shared/src/utils/routes/resolveRoutePathWithExt.ts b/packages/shared/src/utils/routes/resolveRoutePathWithExt.ts new file mode 100644 index 0000000000..a3d9924af8 --- /dev/null +++ b/packages/shared/src/utils/routes/resolveRoutePathWithExt.ts @@ -0,0 +1,2 @@ +export const resolveRoutePathWithExt = (routePath: string): string => + routePath.endsWith('/') ? routePath : routePath + '.html' diff --git a/packages/shared/tests/routes/inferRoutePath.spec.ts b/packages/shared/tests/routes/inferRoutePath.spec.ts index 053fa22a89..71960e6edf 100644 --- a/packages/shared/tests/routes/inferRoutePath.spec.ts +++ b/packages/shared/tests/routes/inferRoutePath.spec.ts @@ -15,19 +15,19 @@ const testCases = [ ['/foo/index.md', '/foo/'], ['/foo/index.html', '/foo/'], ['/foo/index', '/foo/'], - ['README.md', 'index.html'], - ['readme.md', 'index.html'], - ['index.md', 'index.html'], - ['index.html', 'index.html'], - ['index', 'index.html'], + ['README.md', 'index'], + ['readme.md', 'index'], + ['index.md', 'index'], + ['index.html', 'index'], + ['index', 'index'], // absolute non-index - ['/foo', '/foo.html'], - ['/foo.md', '/foo.html'], - ['/foo.html', '/foo.html'], - ['/foo/bar', '/foo/bar.html'], - ['/foo/bar.md', '/foo/bar.html'], - ['/foo/bar.html', '/foo/bar.html'], + ['/foo', '/foo'], + ['/foo.md', '/foo'], + ['/foo.html', '/foo'], + ['/foo/bar', '/foo/bar'], + ['/foo/bar.md', '/foo/bar'], + ['/foo/bar.html', '/foo/bar'], // relative index without current ['foo/', 'foo/'], @@ -38,19 +38,19 @@ const testCases = [ ['foo/index', 'foo/'], // relative non index without current - ['foo', 'foo.html'], - ['foo.md', 'foo.html'], - ['foo.html', 'foo.html'], - ['foo/bar', 'foo/bar.html'], - ['foo/bar.md', 'foo/bar.html'], - ['foo/bar.html', 'foo/bar.html'], + ['foo', 'foo'], + ['foo.md', 'foo'], + ['foo.html', 'foo'], + ['foo/bar', 'foo/bar'], + ['foo/bar.md', 'foo/bar'], + ['foo/bar.html', 'foo/bar'], // unexpected corner cases ['', ''], - ['.md', '.html'], - ['foo/.md', 'foo/.html'], - ['/.md', '/.html'], - ['/foo/.md', '/foo/.html'], + ['.md', ''], + ['foo/.md', 'foo/'], + ['/.md', '/'], + ['/foo/.md', '/foo/'], ] describe('should normalize clean paths correctly', () => { diff --git a/packages/shared/tests/routes/normalizeRoutePath.spec.ts b/packages/shared/tests/routes/normalizeRoutePath.spec.ts index 1732c6d2f6..85fec82d27 100644 --- a/packages/shared/tests/routes/normalizeRoutePath.spec.ts +++ b/packages/shared/tests/routes/normalizeRoutePath.spec.ts @@ -15,19 +15,19 @@ const testCases = [ [['/foo/index.md'], '/foo/'], [['/foo/index.html'], '/foo/'], [['/foo/index'], '/foo/'], - [['README.md'], 'index.html'], - [['readme.md'], 'index.html'], - [['index.md'], 'index.html'], - [['index.html'], 'index.html'], - [['index'], 'index.html'], + [['README.md'], 'index'], + [['readme.md'], 'index'], + [['index.md'], 'index'], + [['index.html'], 'index'], + [['index'], 'index'], // absolute non-index - [['/foo'], '/foo.html'], - [['/foo.md'], '/foo.html'], - [['/foo.html'], '/foo.html'], - [['/foo/bar'], '/foo/bar.html'], - [['/foo/bar.md'], '/foo/bar.html'], - [['/foo/bar.html'], '/foo/bar.html'], + [['/foo'], '/foo'], + [['/foo.md'], '/foo'], + [['/foo.html'], '/foo'], + [['/foo/bar'], '/foo/bar'], + [['/foo/bar.md'], '/foo/bar'], + [['/foo/bar.html'], '/foo/bar'], // relative index without current [['foo/'], 'foo/'], @@ -38,163 +38,163 @@ const testCases = [ [['foo/index'], 'foo/'], // relative non index without current - [['foo'], 'foo.html'], - [['foo.md'], 'foo.html'], - [['foo.html'], 'foo.html'], - [['foo/bar'], 'foo/bar.html'], - [['foo/bar.md'], 'foo/bar.html'], - [['foo/bar.html'], 'foo/bar.html'], + [['foo'], 'foo'], + [['foo.md'], 'foo'], + [['foo.html'], 'foo'], + [['foo/bar'], 'foo/bar'], + [['foo/bar.md'], 'foo/bar'], + [['foo/bar.html'], 'foo/bar'], // relative non index with current - [['foo', '/'], '/foo.html'], - [['foo', '/a.html'], '/foo.html'], - [['foo', '/index.html'], '/foo.html'], - [['foo', '/a/'], '/a/foo.html'], - [['foo', '/a/index.html'], '/a/foo.html'], - [['foo', '/a/b.html'], '/a/foo.html'], - [['foo.md', '/'], '/foo.html'], - [['foo.md', '/a.html'], '/foo.html'], - [['foo.md', '/index.html'], '/foo.html'], - [['foo.md', '/a/'], '/a/foo.html'], - [['foo.md', '/a/index.html'], '/a/foo.html'], - [['foo.md', '/a/b.html'], '/a/foo.html'], - [['foo.html', '/'], '/foo.html'], - [['foo.html', '/a.html'], '/foo.html'], - [['foo.html', '/index.html'], '/foo.html'], - [['foo.html', '/a/'], '/a/foo.html'], - [['foo.html', '/a/index.html'], '/a/foo.html'], - [['foo.html', '/a/b.html'], '/a/foo.html'], - [['foo/bar', '/'], '/foo/bar.html'], - [['foo/bar', '/a.html'], '/foo/bar.html'], - [['foo/bar', '/index.html'], '/foo/bar.html'], - [['foo/bar', '/a/'], '/a/foo/bar.html'], - [['foo/bar', '/a/index.html'], '/a/foo/bar.html'], - [['foo/bar', '/a/b.html'], '/a/foo/bar.html'], - [['foo/bar.md', '/'], '/foo/bar.html'], - [['foo/bar.md', '/a.html'], '/foo/bar.html'], - [['foo/bar.md', '/index.html'], '/foo/bar.html'], - [['foo/bar.md', '/a/'], '/a/foo/bar.html'], - [['foo/bar.md', '/a/index.html'], '/a/foo/bar.html'], - [['foo/bar.md', '/a/b.html'], '/a/foo/bar.html'], - [['foo/bar.html', '/'], '/foo/bar.html'], - [['foo/bar.html', '/a.html'], '/foo/bar.html'], - [['foo/bar.html', '/index.html'], '/foo/bar.html'], - [['foo/bar.html', '/a/'], '/a/foo/bar.html'], - [['foo/bar.html', '/a/index.html'], '/a/foo/bar.html'], - [['foo/bar.html', '/a/b.html'], '/a/foo/bar.html'], - [['./foo', '/'], '/foo.html'], - [['./foo', '/a.html'], '/foo.html'], - [['./foo', '/index.html'], '/foo.html'], - [['./foo', '/a/'], '/a/foo.html'], - [['./foo', '/a/index.html'], '/a/foo.html'], - [['./foo', '/a/b.html'], '/a/foo.html'], - [['./foo.md', '/'], '/foo.html'], - [['./foo.md', '/a.html'], '/foo.html'], - [['./foo.md', '/index.html'], '/foo.html'], - [['./foo.md', '/a/'], '/a/foo.html'], - [['./foo.md', '/a/index.html'], '/a/foo.html'], - [['./foo.md', '/a/b.html'], '/a/foo.html'], - [['./foo.html', '/'], '/foo.html'], - [['./foo.html', '/a.html'], '/foo.html'], - [['./foo.html', '/index.html'], '/foo.html'], - [['./foo.html', '/a/'], '/a/foo.html'], - [['./foo.html', '/a/index.html'], '/a/foo.html'], - [['./foo.html', '/a/b.html'], '/a/foo.html'], - [['./foo/bar', '/'], '/foo/bar.html'], - [['./foo/bar', '/a.html'], '/foo/bar.html'], - [['./foo/bar', '/index.html'], '/foo/bar.html'], - [['./foo/bar', '/a/'], '/a/foo/bar.html'], - [['./foo/bar', '/a/index.html'], '/a/foo/bar.html'], - [['./foo/bar', '/a/b.html'], '/a/foo/bar.html'], - [['./foo/bar.md', '/'], '/foo/bar.html'], - [['./foo/bar.md', '/a.html'], '/foo/bar.html'], - [['./foo/bar.md', '/index.html'], '/foo/bar.html'], - [['./foo/bar.md', '/a/'], '/a/foo/bar.html'], - [['./foo/bar.md', '/a/index.html'], '/a/foo/bar.html'], - [['./foo/bar.md', '/a/b.html'], '/a/foo/bar.html'], - [['./foo/bar.html', '/'], '/foo/bar.html'], - [['./foo/bar.html', '/a.html'], '/foo/bar.html'], - [['./foo/bar.html', '/index.html'], '/foo/bar.html'], - [['./foo/bar.html', '/a/'], '/a/foo/bar.html'], - [['./foo/bar.html', '/a/index.html'], '/a/foo/bar.html'], - [['./foo/bar.html', '/a/b.html'], '/a/foo/bar.html'], - [['../foo', '/a/'], '/foo.html'], - [['../foo', '/a/index.html'], '/foo.html'], - [['../foo', '/a/b.html'], '/foo.html'], - [['../foo.md', '/a/'], '/foo.html'], - [['../foo.md', '/a/index.html'], '/foo.html'], - [['../foo.md', '/a/b.html'], '/foo.html'], - [['../foo.html', '/a/'], '/foo.html'], - [['../foo.html', '/a/index.html'], '/foo.html'], - [['../foo.html', '/a/b.html'], '/foo.html'], - [['../foo/bar', '/a/'], '/foo/bar.html'], - [['../foo/bar', '/a/index.html'], '/foo/bar.html'], - [['../foo/bar', '/a/b.html'], '/foo/bar.html'], - [['../foo/bar.md', '/a/'], '/foo/bar.html'], - [['../foo/bar.md', '/a/index.html'], '/foo/bar.html'], - [['../foo/bar.md', '/a/b.html'], '/foo/bar.html'], - [['../foo/bar.html', '/a/'], '/foo/bar.html'], - [['../foo/bar.html', '/a/index.html'], '/foo/bar.html'], - [['../foo/bar.html', '/a/b.html'], '/foo/bar.html'], + [['foo', '/'], '/foo'], + [['foo', '/a.html'], '/foo'], + [['foo', '/index.html'], '/foo'], + [['foo', '/a/'], '/a/foo'], + [['foo', '/a/index.html'], '/a/foo'], + [['foo', '/a/b.html'], '/a/foo'], + [['foo.md', '/'], '/foo'], + [['foo.md', '/a.html'], '/foo'], + [['foo.md', '/index.html'], '/foo'], + [['foo.md', '/a/'], '/a/foo'], + [['foo.md', '/a/index.html'], '/a/foo'], + [['foo.md', '/a/b.html'], '/a/foo'], + [['foo.html', '/'], '/foo'], + [['foo.html', '/a.html'], '/foo'], + [['foo.html', '/index.html'], '/foo'], + [['foo.html', '/a/'], '/a/foo'], + [['foo.html', '/a/index.html'], '/a/foo'], + [['foo.html', '/a/b.html'], '/a/foo'], + [['foo/bar', '/'], '/foo/bar'], + [['foo/bar', '/a.html'], '/foo/bar'], + [['foo/bar', '/index.html'], '/foo/bar'], + [['foo/bar', '/a/'], '/a/foo/bar'], + [['foo/bar', '/a/index.html'], '/a/foo/bar'], + [['foo/bar', '/a/b.html'], '/a/foo/bar'], + [['foo/bar.md', '/'], '/foo/bar'], + [['foo/bar.md', '/a.html'], '/foo/bar'], + [['foo/bar.md', '/index.html'], '/foo/bar'], + [['foo/bar.md', '/a/'], '/a/foo/bar'], + [['foo/bar.md', '/a/index.html'], '/a/foo/bar'], + [['foo/bar.md', '/a/b.html'], '/a/foo/bar'], + [['foo/bar.html', '/'], '/foo/bar'], + [['foo/bar.html', '/a.html'], '/foo/bar'], + [['foo/bar.html', '/index.html'], '/foo/bar'], + [['foo/bar.html', '/a/'], '/a/foo/bar'], + [['foo/bar.html', '/a/index.html'], '/a/foo/bar'], + [['foo/bar.html', '/a/b.html'], '/a/foo/bar'], + [['./foo', '/'], '/foo'], + [['./foo', '/a.html'], '/foo'], + [['./foo', '/index.html'], '/foo'], + [['./foo', '/a/'], '/a/foo'], + [['./foo', '/a/index.html'], '/a/foo'], + [['./foo', '/a/b.html'], '/a/foo'], + [['./foo.md', '/'], '/foo'], + [['./foo.md', '/a.html'], '/foo'], + [['./foo.md', '/index.html'], '/foo'], + [['./foo.md', '/a/'], '/a/foo'], + [['./foo.md', '/a/index.html'], '/a/foo'], + [['./foo.md', '/a/b.html'], '/a/foo'], + [['./foo.html', '/'], '/foo'], + [['./foo.html', '/a.html'], '/foo'], + [['./foo.html', '/index.html'], '/foo'], + [['./foo.html', '/a/'], '/a/foo'], + [['./foo.html', '/a/index.html'], '/a/foo'], + [['./foo.html', '/a/b.html'], '/a/foo'], + [['./foo/bar', '/'], '/foo/bar'], + [['./foo/bar', '/a.html'], '/foo/bar'], + [['./foo/bar', '/index.html'], '/foo/bar'], + [['./foo/bar', '/a/'], '/a/foo/bar'], + [['./foo/bar', '/a/index.html'], '/a/foo/bar'], + [['./foo/bar', '/a/b.html'], '/a/foo/bar'], + [['./foo/bar.md', '/'], '/foo/bar'], + [['./foo/bar.md', '/a.html'], '/foo/bar'], + [['./foo/bar.md', '/index.html'], '/foo/bar'], + [['./foo/bar.md', '/a/'], '/a/foo/bar'], + [['./foo/bar.md', '/a/index.html'], '/a/foo/bar'], + [['./foo/bar.md', '/a/b.html'], '/a/foo/bar'], + [['./foo/bar.html', '/'], '/foo/bar'], + [['./foo/bar.html', '/a.html'], '/foo/bar'], + [['./foo/bar.html', '/index.html'], '/foo/bar'], + [['./foo/bar.html', '/a/'], '/a/foo/bar'], + [['./foo/bar.html', '/a/index.html'], '/a/foo/bar'], + [['./foo/bar.html', '/a/b.html'], '/a/foo/bar'], + [['../foo', '/a/'], '/foo'], + [['../foo', '/a/index.html'], '/foo'], + [['../foo', '/a/b.html'], '/foo'], + [['../foo.md', '/a/'], '/foo'], + [['../foo.md', '/a/index.html'], '/foo'], + [['../foo.md', '/a/b.html'], '/foo'], + [['../foo.html', '/a/'], '/foo'], + [['../foo.html', '/a/index.html'], '/foo'], + [['../foo.html', '/a/b.html'], '/foo'], + [['../foo/bar', '/a/'], '/foo/bar'], + [['../foo/bar', '/a/index.html'], '/foo/bar'], + [['../foo/bar', '/a/b.html'], '/foo/bar'], + [['../foo/bar.md', '/a/'], '/foo/bar'], + [['../foo/bar.md', '/a/index.html'], '/foo/bar'], + [['../foo/bar.md', '/a/b.html'], '/foo/bar'], + [['../foo/bar.html', '/a/'], '/foo/bar'], + [['../foo/bar.html', '/a/index.html'], '/foo/bar'], + [['../foo/bar.html', '/a/b.html'], '/foo/bar'], // absolute non index with current - [['/foo', '/'], '/foo.html'], - [['/foo', '/a.html'], '/foo.html'], - [['/foo', '/index.html'], '/foo.html'], - [['/foo', '/a/'], '/foo.html'], - [['/foo', '/a/index.html'], '/foo.html'], - [['/foo', '/a/b.html'], '/foo.html'], - [['/foo.md', '/'], '/foo.html'], - [['/foo.md', '/a.html'], '/foo.html'], - [['/foo.md', '/index.html'], '/foo.html'], - [['/foo.md', '/a/'], '/foo.html'], - [['/foo.md', '/a/index.html'], '/foo.html'], - [['/foo.md', '/a/b.html'], '/foo.html'], - [['/foo.html', '/'], '/foo.html'], - [['/foo.html', '/a.html'], '/foo.html'], - [['/foo.html', '/index.html'], '/foo.html'], - [['/foo.html', '/a/'], '/foo.html'], - [['/foo.html', '/a/index.html'], '/foo.html'], - [['/foo.html', '/a/b.html'], '/foo.html'], - [['/foo/bar', '/'], '/foo/bar.html'], - [['/foo/bar', '/a.html'], '/foo/bar.html'], - [['/foo/bar', '/index.html'], '/foo/bar.html'], - [['/foo/bar', '/a/'], '/foo/bar.html'], - [['/foo/bar', '/a/index.html'], '/foo/bar.html'], - [['/foo/bar', '/a/b.html'], '/foo/bar.html'], - [['/foo/bar.md', '/'], '/foo/bar.html'], - [['/foo/bar.md', '/a.html'], '/foo/bar.html'], - [['/foo/bar.md', '/index.html'], '/foo/bar.html'], - [['/foo/bar.md', '/a/'], '/foo/bar.html'], - [['/foo/bar.md', '/a/index.html'], '/foo/bar.html'], - [['/foo/bar.md', '/a/b.html'], '/foo/bar.html'], - [['/foo/bar.html', '/'], '/foo/bar.html'], - [['/foo/bar.html', '/a.html'], '/foo/bar.html'], - [['/foo/bar.html', '/index.html'], '/foo/bar.html'], - [['/foo/bar.html', '/a/'], '/foo/bar.html'], - [['/foo/bar.html', '/a/index.html'], '/foo/bar.html'], - [['/foo/bar.html', '/a/b.html'], '/foo/bar.html'], + [['/foo', '/'], '/foo'], + [['/foo', '/a.html'], '/foo'], + [['/foo', '/index.html'], '/foo'], + [['/foo', '/a/'], '/foo'], + [['/foo', '/a/index.html'], '/foo'], + [['/foo', '/a/b.html'], '/foo'], + [['/foo.md', '/'], '/foo'], + [['/foo.md', '/a.html'], '/foo'], + [['/foo.md', '/index.html'], '/foo'], + [['/foo.md', '/a/'], '/foo'], + [['/foo.md', '/a/index.html'], '/foo'], + [['/foo.md', '/a/b.html'], '/foo'], + [['/foo.html', '/'], '/foo'], + [['/foo.html', '/a.html'], '/foo'], + [['/foo.html', '/index.html'], '/foo'], + [['/foo.html', '/a/'], '/foo'], + [['/foo.html', '/a/index.html'], '/foo'], + [['/foo.html', '/a/b.html'], '/foo'], + [['/foo/bar', '/'], '/foo/bar'], + [['/foo/bar', '/a.html'], '/foo/bar'], + [['/foo/bar', '/index.html'], '/foo/bar'], + [['/foo/bar', '/a/'], '/foo/bar'], + [['/foo/bar', '/a/index.html'], '/foo/bar'], + [['/foo/bar', '/a/b.html'], '/foo/bar'], + [['/foo/bar.md', '/'], '/foo/bar'], + [['/foo/bar.md', '/a.html'], '/foo/bar'], + [['/foo/bar.md', '/index.html'], '/foo/bar'], + [['/foo/bar.md', '/a/'], '/foo/bar'], + [['/foo/bar.md', '/a/index.html'], '/foo/bar'], + [['/foo/bar.md', '/a/b.html'], '/foo/bar'], + [['/foo/bar.html', '/'], '/foo/bar'], + [['/foo/bar.html', '/a.html'], '/foo/bar'], + [['/foo/bar.html', '/index.html'], '/foo/bar'], + [['/foo/bar.html', '/a/'], '/foo/bar'], + [['/foo/bar.html', '/a/index.html'], '/foo/bar'], + [['/foo/bar.html', '/a/b.html'], '/foo/bar'], // only hash and query [[''], ''], // unexpected corner cases - [['.md'], '.html'], - [['foo/.md'], 'foo/.html'], - [['/.md'], '/.html'], - [['/foo/.md'], '/foo/.html'], - [['.md', '/a/'], '/a/.html'], - [['foo/.md', '/a/'], '/a/foo/.html'], - [['/.md', '/a/'], '/.html'], - [['/foo/.md', '/a/'], '/foo/.html'], - [['.md', '/a/index.html'], '/a/.html'], - [['foo/.md', '/a/index.html'], '/a/foo/.html'], - [['/.md', '/a/index.html'], '/.html'], - [['/foo/.md', '/a/index.html'], '/foo/.html'], - [['.md', '/a/b.html'], '/a/.html'], - [['foo/.md', '/a/b.html'], '/a/foo/.html'], - [['/.md', '/a/b.html'], '/.html'], - [['/foo/.md', '/a/b.html'], '/foo/.html'], + [['.md'], ''], + [['foo/.md'], 'foo/'], + [['/.md'], '/'], + [['/foo/.md'], '/foo/'], + [['.md', '/a/'], '/a/'], + [['foo/.md', '/a/'], '/a/foo/'], + [['/.md', '/a/'], '/'], + [['/foo/.md', '/a/'], '/foo/'], + [['.md', '/a/index.html'], '/a/'], + [['foo/.md', '/a/index.html'], '/a/foo/'], + [['/.md', '/a/index.html'], '/'], + [['/foo/.md', '/a/index.html'], '/foo/'], + [['.md', '/a/b.html'], '/a/'], + [['foo/.md', '/a/b.html'], '/a/foo/'], + [['/.md', '/a/b.html'], '/'], + [['/foo/.md', '/a/b.html'], '/foo/'], ] describe('should normalize clean paths correctly', () => { diff --git a/packages/shared/tests/routes/resolveRoutePathInfo.spec.ts b/packages/shared/tests/routes/resolvePathInfo.spec.ts similarity index 75% rename from packages/shared/tests/routes/resolveRoutePathInfo.spec.ts rename to packages/shared/tests/routes/resolvePathInfo.spec.ts index 5577bd8c09..a71f71b5e0 100644 --- a/packages/shared/tests/routes/resolveRoutePathInfo.spec.ts +++ b/packages/shared/tests/routes/resolvePathInfo.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { resolveRoutePathInfo } from '../../src/index.js' +import { resolvePathInfo } 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 path info correctly', () => { +describe('should resolve path info correctly', () => { testCases.forEach(([source, expected]) => { it(`${source} -> ${expected}`, () => { - expect(resolveRoutePathInfo(source)).toEqual(expected) + expect(resolvePathInfo(source)).toEqual(expected) }) }) })