Skip to content

Commit

Permalink
feat: support clean url and improve route resolve
Browse files Browse the repository at this point in the history
  • Loading branch information
Mister-Hope committed May 22, 2024
1 parent 31ac4c9 commit 8a0c158
Show file tree
Hide file tree
Showing 26 changed files with 336 additions and 258 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -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'],
Expand Down
1 change: 1 addition & 0 deletions packages/bundler-vite/src/plugins/vuepressMainPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/bundler-webpack/src/config/handlePluginDefine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/dev/watchPageFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
16 changes: 9 additions & 7 deletions packages/client/src/router/resolveRoute.ts
Original file line number Diff line number Diff line change
@@ -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<T extends RouteMeta = RouteMeta>
extends Route<T> {
Expand All @@ -17,14 +17,16 @@ export const resolveRoute = <T extends RouteMeta = RouteMeta>(
currentPath?: string,
): ResolvedRoute<T> => {
// 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,
Expand All @@ -33,7 +35,7 @@ export const resolveRoute = <T extends RouteMeta = RouteMeta>(
}

return {
...routes.value[routePath],
...routes.value[routeKey],
path: routeFullPath,
notFound: false,
} as ResolvedRoute<T>
Expand Down
4 changes: 2 additions & 2 deletions packages/client/src/router/resolveRouteFullPath.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resolveRoutePathInfo } from '@vuepress/shared'
import { resolvePathInfo } from '@vuepress/shared'
import { resolveRoutePath } from './resolveRoutePath.js'

/**
Expand All @@ -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
}
34 changes: 34 additions & 0 deletions packages/client/src/router/resolveRouteKey.ts
Original file line number Diff line number Diff line change
@@ -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
}
29 changes: 5 additions & 24 deletions packages/client/src/router/resolveRoutePath.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
}
1 change: 1 addition & 0 deletions packages/client/types.d.ts
Original file line number Diff line number Diff line change
@@ -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<string, any>
declare const __VUE_OPTIONS_API__: boolean
Expand Down
19 changes: 15 additions & 4 deletions packages/core/src/app/resolveAppOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => ({
Expand All @@ -65,8 +73,11 @@ export const resolveAppOptions = ({
bundler,
debug,
markdown,
pagePatterns,
permalinkPattern,
route: {
cleanUrl,
pagePatterns,
permalinkPattern,
},
plugins,
theme,
})
6 changes: 3 additions & 3 deletions packages/core/src/app/resolveAppPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const resolveAppPages = async (app: App): Promise<Page[]> => {
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(),
})
Expand All @@ -22,7 +22,7 @@ export const resolveAppPages = async (app: App): Promise<Page[]> => {
)

// 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) {
Expand All @@ -32,7 +32,7 @@ export const resolveAppPages = async (app: App): Promise<Page[]> => {
else {
pages.push(
await createPage(app, {
path: '/404.html',
path: '/404',
frontmatter: { layout: 'NotFound' },
content: '404 Not Found',
}),
Expand Down
12 changes: 11 additions & 1 deletion packages/core/src/page/resolvePagePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('/'),
)
}
2 changes: 1 addition & 1 deletion packages/core/src/page/resolvePagePermalink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 20 additions & 5 deletions packages/core/src/types/app/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -14,11 +20,9 @@ export interface AppConfigCommon extends Partial<SiteData> {
temp?: string
cache?: string
public?: string

debug?: boolean
markdown?: MarkdownOptions
pagePatterns?: string[]
permalinkPattern?: string | null
route?: RouteOptions
bundler: Bundler
theme: Theme
plugins?: PluginConfig
Expand Down Expand Up @@ -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<AppConfig>
export type AppOptions = Required<
AppConfigCommon & AppConfigDev & AppConfigBuild
> & {
route: Required<RouteOptions>
}
7 changes: 5 additions & 2 deletions packages/core/tests/app/resolveAppOptions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
),
Expand Down
12 changes: 6 additions & 6 deletions packages/core/tests/app/resolveAppPages.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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')
Expand Down
4 changes: 2 additions & 2 deletions packages/core/tests/page/resolvePagePath.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const testCases: [
},
},
],
'/options.html',
'/options',
],
// use permalink
[
Expand Down Expand Up @@ -92,7 +92,7 @@ const testCases: [
options: {},
},
],
'/inferred.html',
'/inferred',
],
]

Expand Down
Loading

0 comments on commit 8a0c158

Please sign in to comment.