feat: add option to customize title template

close #303
pull/639/head
Kia King Ishii 2 years ago
parent 86b49dde6b
commit 67e77f7871

@ -45,7 +45,7 @@ export default {
- Type: `string`
- Default: `VitePress`
Title for the site. This will be the suffix for all page titles, and displayed in the nav bar.
Title for the site. This will be displayed in the nav bar. Also used as the suffix for all page titles unless `titleTemplate` is defined.
```ts
export default {
@ -53,6 +53,21 @@ export default {
}
```
## titleTemplate
- Type: `string | boolean`
The suffix for the title. For example, if you set `title` as `VitePress` and set `titleTemplate` as `My Site`, the html title becomes `VitePress | My Site`.
Set `false` to disable the feature. If the option is `undefined`, then the value of `title` option will be used.
```ts
export default {
title: 'VitePress',
titleTemplate: 'Vite & Vue powered static site generator.'
}
```
## description
- Type: `string`

@ -27,6 +27,19 @@ title: VitePress
---
```
## titleTemplate
- Type: `string | boolean`
The suffix for the title. It's same as [config.titleTemplate](../config/app-configs#titleTemplate), and it overrides the app config.
```yaml
---
title: VitePress,
titleTemplate: Vite & Vue powered static site generator.
---
```
## description
- Type: `string`

@ -1,6 +1,9 @@
---
layout: home
title: VitePress
titleTemplate: Vite & Vue powered static site generator.
hero:
name: VitePress
text: Vite & Vue powered static site generator.

@ -1,5 +1,5 @@
import { watchEffect, Ref } from 'vue'
import { HeadConfig, SiteData } from '../../shared'
import { HeadConfig, SiteData, createTitle } from '../../shared'
import { Route } from '../router'
export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
@ -56,12 +56,12 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
watchEffect(() => {
const pageData = route.data
const siteData = siteDataByRouteRef.value
const pageTitle = pageData && pageData.title
const pageDescription = pageData && pageData.description
const frontmatterHead = pageData && pageData.frontmatter.head
// update title and description
document.title = (pageTitle ? pageTitle + ` | ` : ``) + siteData.title
document.title = createTitle(siteData, pageData)
document
.querySelector(`meta[name=description]`)!
.setAttribute('content', pageDescription || siteData.description)

@ -1,7 +1,12 @@
import { InjectionKey, Ref, shallowRef, readonly, computed, inject } from 'vue'
import { Route } from './router'
import serializedSiteData from '@siteData'
import { resolveSiteDataByRoute, PageData, SiteData } from '../shared'
import {
PageData,
SiteData,
resolveSiteDataByRoute,
createTitle
} from '../shared'
import { withBase } from './utils'
export const dataSymbol: InjectionKey<VitePressData> = Symbol()
@ -54,9 +59,7 @@ export function initData(route: Route): VitePressData {
return withBase(path || '/')
}),
title: computed(() => {
return route.data.title
? route.data.title + ' | ' + site.value.title
: site.value.title
return createTitle(site.value, route.data)
}),
description: computed(() => {
return route.data.description || site.value.description

@ -1,7 +1,7 @@
import path from 'path'
import fs from 'fs-extra'
import { SiteConfig, resolveSiteDataByRoute } from '../config'
import { HeadConfig } from '../shared'
import { HeadConfig, createTitle } from '../shared'
import { normalizePath, transformWithEsbuild } from 'vite'
import { RollupOutput, OutputChunk, OutputAsset } from 'rollup'
import { slash } from '../utils/slash'
@ -92,11 +92,7 @@ export async function renderPage(
? `<link rel="stylesheet" href="${siteData.base}${cssChunk.fileName}">`
: ''
const title: string =
pageData.title && pageData.title !== 'Home'
? `${pageData.title} | ${siteData.title}`
: siteData.title
const title: string = createTitle(siteData, pageData)
const description: string = pageData.description || siteData.description
const head = addSocialTags(

@ -32,6 +32,7 @@ export interface UserConfig<ThemeConfig = any> {
base?: string
lang?: string
title?: string
titleTemplate?: string | boolean
description?: string
head?: HeadConfig[]
appearance?: boolean
@ -248,6 +249,7 @@ export async function resolveSiteData(
return {
lang: userConfig.lang || 'en-US',
title: userConfig.title || 'VitePress',
titleTemplate: userConfig.titleTemplate,
description: userConfig.description || 'A VitePress site',
base: userConfig.base ? userConfig.base.replace(/([^/])$/, '$1/') : '/',
head: resolveSiteDataHead(userConfig),

@ -134,6 +134,7 @@ export function createMarkdownToVueRenderFn(
const pageData: PageData = {
title: inferTitle(frontmatter, content),
titleTemplate: frontmatter.titleTemplate,
description: inferDescription(frontmatter),
frontmatter,
headers: data.headers || [],
@ -197,17 +198,17 @@ function genPageDataCode(tags: string[], data: PageData) {
return tags
}
const inferTitle = (frontmatter: any, content: string) => {
const inferTitle = (frontmatter: Record<string, any>, content: string) => {
if (frontmatter.title) {
return deeplyParseHeader(frontmatter.title)
}
if (frontmatter.home) {
return 'Home'
}
const match = content.match(/^\s*#+\s+(.*)/m)
if (match) {
return deeplyParseHeader(match[1].trim())
}
return ''
}

@ -1,4 +1,4 @@
import { LocaleConfig, SiteData } from '../../types/shared'
import { SiteData, PageData, LocaleConfig } from '../../types/shared'
export type {
SiteData,
@ -83,6 +83,36 @@ export function resolveSiteDataByRoute(
})
}
/**
* Create the page title string based on configs.
*/
export function createTitle(siteData: SiteData, pageData: PageData): string {
const title = pageData.title || siteData.title
const template = pageData.titleTemplate ?? siteData.titleTemplate
const templateString = createTitleTemplate(siteData.title, template)
return `${title}${templateString}`
}
function createTitleTemplate(
siteTitle: string,
template?: string | boolean
): string {
if (template === false) {
return ''
}
if (template === true || template === undefined) {
return ` | ${siteTitle}`
}
if (siteTitle === template) {
return ''
}
return ` | ${template}`
}
/**
* Clean up the route by removing the `base` path if it's set in config.
*/

3
types/shared.d.ts vendored

@ -5,6 +5,7 @@ export { DefaultTheme } from './default-theme'
export interface PageData {
relativePath: string
title: string
titleTemplate?: string | boolean
description: string
headers: Header[]
frontmatter: Record<string, any>
@ -28,6 +29,7 @@ export interface SiteData<ThemeConfig = any> {
lang: string
title: string
titleTemplate?: string | boolean
description: string
head: HeadConfig[]
appearance: boolean
@ -64,6 +66,7 @@ export type HeadConfig =
export interface LocaleConfig {
lang: string
title?: string
titleTemplate?: string | boolean
description?: string
head?: HeadConfig[]
label?: string

Loading…
Cancel
Save