mirror of https://github.com/vuejs/vitepress
feat: sitemap generation (#2691)
parent
b61f36d853
commit
5563695b15
@ -0,0 +1,53 @@
|
|||||||
|
# Sitemap Generation
|
||||||
|
|
||||||
|
VitePress comes with out-of-the-box support for generating a `sitemap.xml` file for your site. To enable it, add the following to your `.vitepress/config.js`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
sitemap: {
|
||||||
|
hostname: 'https://example.com'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
To have `<lastmod>` tags in your `sitemap.xml`, you can enable the [`lastUpdated`](../reference/default-theme-last-updated) option.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
Sitemap support is powered by the [`sitemap`](https://www.npmjs.com/package/sitemap) module. You can pass any options supported by it to the `sitemap` option in your config file. These will be passed directly to the `SitemapStream` constructor. Refer to the [`sitemap` documentation](https://www.npmjs.com/package/sitemap#options-you-can-pass) for more details. Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
sitemap: {
|
||||||
|
hostname: 'https://example.com',
|
||||||
|
lastmodDateOnly: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## `transformItems` Hook
|
||||||
|
|
||||||
|
You can use the `sitemap.transformItems` hook to modify the sitemap items before they are written to the `sitemap.xml` file. This hook is called with an array of sitemap items and expects an array of sitemap items to be returned. Example:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineConfig } from 'vitepress'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
sitemap: {
|
||||||
|
hostname: 'https://example.com',
|
||||||
|
transformItems: (items) => {
|
||||||
|
// add new items or modify/filter existing items
|
||||||
|
items.push({
|
||||||
|
url: '/extra-page',
|
||||||
|
changefreq: 'monthly',
|
||||||
|
priority: 0.8
|
||||||
|
})
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
@ -0,0 +1,60 @@
|
|||||||
|
import fs from 'fs-extra'
|
||||||
|
import path from 'path'
|
||||||
|
import {
|
||||||
|
SitemapStream,
|
||||||
|
type EnumChangefreq,
|
||||||
|
type Img,
|
||||||
|
type LinkItem,
|
||||||
|
type NewsItem
|
||||||
|
} from 'sitemap'
|
||||||
|
import type { SiteConfig } from '../config'
|
||||||
|
import { getGitTimestamp } from '../utils/getGitTimestamp'
|
||||||
|
import { task } from '../utils/task'
|
||||||
|
|
||||||
|
export async function generateSitemap(siteConfig: SiteConfig) {
|
||||||
|
if (!siteConfig.sitemap?.hostname) return
|
||||||
|
|
||||||
|
await task('generating sitemap', async () => {
|
||||||
|
let items: SitemapItem[] = await Promise.all(
|
||||||
|
siteConfig.pages.map(async (page) => {
|
||||||
|
//
|
||||||
|
let url = siteConfig.rewrites.map[page] || page
|
||||||
|
url = url.replace(/(^|\/)?index.md$/, '$1')
|
||||||
|
url = url.replace(/\.md$/, siteConfig.cleanUrls ? '' : '.html')
|
||||||
|
|
||||||
|
const lastmod = siteConfig.lastUpdated && (await getGitTimestamp(page))
|
||||||
|
return lastmod ? { url, lastmod } : { url }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
items = items.sort((a, b) => a.url.localeCompare(b.url))
|
||||||
|
items = (await siteConfig.sitemap?.transformItems?.(items)) || items
|
||||||
|
|
||||||
|
const sitemapStream = new SitemapStream(siteConfig.sitemap)
|
||||||
|
const sitemapPath = path.join(siteConfig.outDir, 'sitemap.xml')
|
||||||
|
const writeStream = fs.createWriteStream(sitemapPath)
|
||||||
|
|
||||||
|
sitemapStream.pipe(writeStream)
|
||||||
|
items.forEach((item) => sitemapStream.write(item))
|
||||||
|
sitemapStream.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================== Patched Types ===============================
|
||||||
|
|
||||||
|
export interface SitemapItem {
|
||||||
|
lastmod?: string | number | Date
|
||||||
|
changefreq?: `${EnumChangefreq}`
|
||||||
|
fullPrecisionPriority?: boolean
|
||||||
|
priority?: number
|
||||||
|
news?: NewsItem
|
||||||
|
expires?: string
|
||||||
|
androidLink?: string
|
||||||
|
ampLink?: string
|
||||||
|
url: string
|
||||||
|
video?: any
|
||||||
|
img?: string | Img | (string | Img)[]
|
||||||
|
links?: LinkItem[]
|
||||||
|
lastmodfile?: string | Buffer | URL
|
||||||
|
lastmodISO?: string
|
||||||
|
lastmodrealtime?: boolean
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
import ora from 'ora'
|
||||||
|
|
||||||
|
export const okMark = '\x1b[32m✓\x1b[0m'
|
||||||
|
export const failMark = '\x1b[31m✖\x1b[0m'
|
||||||
|
|
||||||
|
export async function task(taskName: string, task: () => Promise<void>) {
|
||||||
|
const spinner = ora({ discardStdin: false })
|
||||||
|
spinner.start(taskName + '...')
|
||||||
|
|
||||||
|
try {
|
||||||
|
await task()
|
||||||
|
} catch (e) {
|
||||||
|
spinner.stopAndPersist({ symbol: failMark })
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.stopAndPersist({ symbol: okMark })
|
||||||
|
}
|
Loading…
Reference in new issue