Skip to content

Commit

Permalink
feat: add route option
Browse files Browse the repository at this point in the history
  • Loading branch information
Mister-Hope committed Apr 17, 2024
1 parent 5a4700a commit f05b2e9
Show file tree
Hide file tree
Showing 14 changed files with 89 additions and 25 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 @@ -203,6 +203,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
6 changes: 5 additions & 1 deletion packages/client/src/router/resolveRoutePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { redirects, routes } from '../internal/routes.js'
*/
export const resolveRoutePath = (path: string, current?: string): string => {
// normalized path
const normalizedPath = normalizeRoutePath(path, current)
const normalizedPath = normalizeRoutePath(
path,
current,
__VUEPRESS_CLEAN_URL__,
)
if (routes.value[normalizedPath]) return normalizedPath

// encoded path
Expand Down
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
16 changes: 13 additions & 3 deletions packages/core/src/app/prepare/prepareRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@ if (import.meta.hot) {
/**
* Resolve page redirects
*/
const resolvePageRedirects = ({ path, pathInferred }: Page): string[] => {
const resolvePageRedirects = (
app: App,
{ path, pathInferred }: Page,
): string[] => {
// paths that should redirect to this page, use set to dedupe
const redirectsSet = new Set<string>()

// add redirect to the set when the redirect could not be normalized & encoded to the page path
const addRedirect = (redirect: string): void => {
const normalizedPath = normalizeRoutePath(redirect)
const normalizedPath = normalizeRoutePath(
redirect,
'',
app.options.route.cleanUrl,
)
if (normalizedPath === path) return

const encodedPath = encodeURI(normalizedPath)
Expand All @@ -56,7 +63,10 @@ export const redirects = JSON.parse(${JSON.stringify(
JSON.stringify(
Object.fromEntries(
app.pages.flatMap((page) =>
resolvePageRedirects(page).map((redirect) => [redirect, page.path]),
resolvePageRedirects(app, page).map((redirect) => [
redirect,
page.path,
]),
),
),
),
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,
})
2 changes: 1 addition & 1 deletion 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 Down
12 changes: 9 additions & 3 deletions packages/core/src/page/inferPagePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ export const inferPagePath = ({

// infer page route path from file path
// foo/bar.md -> /foo/bar.html
const pathInferred = ensureLeadingSlash(filePathRelative)
.replace(/\.md$/, '.html')
.replace(/\/(README|index).html$/i, '/')
let pathInferred = ensureLeadingSlash(filePathRelative).replace(
/\/(README|index).md$/i,
'/',
)

if (pathInferred.endsWith('.md'))
pathInferred =
pathInferred.substring(0, pathInferred.length - 3) +
(app.options.route.cleanUrl ? '' : '.html')

// resolve page locale path
const pathLocale = resolveLocalePath(app.siteData.locales, pathInferred)
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
6 changes: 4 additions & 2 deletions packages/shared/src/utils/inferRoutePath.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const inferRoutePath = (path: string): string => {
export const inferRoutePath = (path: string, cleanUrl = false): string => {
// if the pathname is empty or ends with `/`, return as is
if (!path || path.endsWith('/')) return path

Expand All @@ -19,5 +19,7 @@ export const inferRoutePath = (path: string): string => {
routePath = routePath.substring(0, routePath.length - 10)
}

return routePath
return cleanUrl && routePath.endsWith('html')
? routePath.substring(0, routePath.length - 5)
: routePath
}
10 changes: 7 additions & 3 deletions packages/shared/src/utils/normalizeRoutePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ const FAKE_HOST = 'http://.'
/**
* Normalize the given path to the final route path
*/
export const normalizeRoutePath = (path: string, current?: string): string => {
export const normalizeRoutePath = (
path: string,
current = '',
cleanUrl = false,
): 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
return inferRoutePath(pathname, cleanUrl) + search + hash
}

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

return inferRoutePath(pathname) + queryAndHash.join('')
return inferRoutePath(pathname, cleanUrl) + queryAndHash.join('')
}

0 comments on commit f05b2e9

Please sign in to comment.