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