Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sitemap generation #520

Closed
4 tasks done
JanWerder opened this issue Feb 4, 2022 · 14 comments · Fixed by #2691
Closed
4 tasks done

Sitemap generation #520

JanWerder opened this issue Feb 4, 2022 · 14 comments · Fixed by #2691
Labels
build Related to the build system enhancement New feature or request has-workaround Has workaround, low priority

Comments

@JanWerder
Copy link

Is your feature request related to a problem? Please describe.

vitepress does not generate a sitemap.xml

Describe the solution you'd like

vitepress should generate a sitemap based on the config.js

Describe alternatives you've considered

The user could create a sitemap by hand

Additional context

No response

Validations

@aboutsimon
Copy link

Like you hinted at, in the alternatives, it's fairly straightforward to build out your own sitemap generation. So just for reference, an implementation idea that might work for you, using sitemap.. This assumes you have srcDir: './pages' set.

const fs = require('fs')
const path = require('path')
const fg = require('fast-glob')
const { SitemapStream, streamToPromise } = require('sitemap')

console.log('Start building sitemap..')

const linksStream = fg.stream(['./pages/*.md'])
  .map((filePath) => ({ url: filePath.replace('pages/', '').replace(/\.md$/, '.html') }))

const sitemapStream = new SitemapStream({ hostname: 'https://www.example.org' })

// Return a promise that resolves with your XML string
streamToPromise(linksStream.pipe(sitemapStream)).then((sitemap) => {
  fs.writeFileSync(
    path.resolve(__dirname, '../pages/public/sitemap.xml'),
    sitemap,
  )
})

Note that this uses Stream#readable.map, which is a brand new node feature, but you also implement it differently, if you don't want to rely on a experimental api.
It's also possible to use something like gray-matter, to include your meta data in your sitemaps, if needed.

@jbaubree
Copy link

If it can help, i have done a sitemap.xml and robots.txt generator using typescript: sitemap-ts.
This plugin was created and externalized from vite-ssg-sitemap ans is also used in vite-plugin-sitemap.

@toniengelhardt
Copy link

@jbaubree amazing, I'll check it out

@kiaking kiaking added the enhancement New feature or request label May 23, 2022
@lmtr0
Copy link

lmtr0 commented Aug 11, 2022

any status on this?

@jbaubree
Copy link

@lmtr0
We need to wait next release of Vitepress with this pushed #709.
We will be able to configure like that :

// .vitepress/config.ts
import { version } from '../../../../packages/snap-design/package.json'
import { generateSitemap as sitemap } from 'sitemap-ts'

export default {
  vite: {
    buildEnd: () => {
      sitemap()
    },
  },
}

@lmtr0
Copy link

lmtr0 commented Aug 11, 2022

understood, for now a simple script solves my problem, thanks

@brc-dd
Copy link
Member

brc-dd commented Sep 1, 2022

On newer versions of VitePress this can be done (there is no need to traverse the dist directory):

import { createWriteStream } from 'node:fs'
import { resolve } from 'node:path'
import { SitemapStream } from 'sitemap'
import { defineConfig } from 'vitepress'

const links = []

export default {
  // ...

  transformHtml: (_, id, { pageData }) => {
    if (!/[\\/]404\.html$/.test(id))
      links.push({
        // you might need to change this if not using clean urls mode
        url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'),
        lastmod: pageData.lastUpdated
      })
  },

  buildEnd: ({ outDir }) => {
    const sitemap = new SitemapStream({ hostname: 'https://vitepress.vuejs.org/' })
    const writeStream = createWriteStream(resolve(outDir, 'sitemap.xml'))
    sitemap.pipe(writeStream)
    links.forEach((link) => sitemap.write(link))
    sitemap.end()
  }
}

@jk2K
Copy link

jk2K commented Oct 1, 2022

if not using clean urls mode, you can use this transformHtml

transformHtml: (_, id, { pageData }) => {
    if (!/[\\/]404\.html$/.test(id))
      links.push({
        url: pageData.relativePath.replace(/\.md$/, '.html'),
        lastmod: pageData.lastUpdated
      })
  },

@mil7
Copy link

mil7 commented Nov 3, 2022

Since release of 1.0.0-alpha.23 the script above does not create a file anymore even though the script runs through as far as I can judge.

Could it be that the buildEnd hook behaves differently than before? The bugfix "build: explicitly exit process after build to prevent hangup" sounds suspicious.

@brc-dd
Copy link
Member

brc-dd commented Nov 3, 2022

Ah right, it is exiting the process before stream can finish. Try this for now:

  buildEnd: async ({ outDir }) => {
    const sitemap = new SitemapStream({
      hostname: 'https://vitepress.vuejs.org/'
    })
    const writeStream = createWriteStream(resolve(outDir, 'sitemap.xml'))
    sitemap.pipe(writeStream)
    links.forEach((link) => sitemap.write(link))
    sitemap.end()
    await new Promise((r) => writeStream.on('finish', r))
  }

brc-dd added a commit that referenced this issue Nov 3, 2022
This is exiting the process before streams can finish (#520 (comment)).
@brc-dd brc-dd added build Related to the build system has-workaround Has workaround, low priority labels Jan 4, 2023
@MarkusKeck
Copy link
Collaborator

@brc-dd Maybe it would be useful if you can specify in the config file if a sitemap should be generated automatically or not.

@erbill
Copy link

erbill commented Apr 6, 2023

@brc-dd Maybe it would be useful if you can specify in the config file if a sitemap should be generated automatically or not.

any updates on this?

now I'm at "vitepress": "^1.0.0-alpha.27",
"vue": "^3.2.41"

    Which way to make sitemaps is the most beginner friendly method?

adrianjost added a commit to adrianjost/random-notes that referenced this issue Apr 7, 2023
adrianjost added a commit to adrianjost/random-notes that referenced this issue Apr 7, 2023
@yangwao
Copy link

yangwao commented May 28, 2023

Which way to make sitemaps is the most beginner friendly method?

Someone might find it usefull as people want to come and grab working snippet.
I'm on "vitepress": "1.0.0-alpha.75" and seems it generates good sitemap.xml

ht for @brc-dd updates!

import { createWriteStream } from 'node:fs'
import { resolve } from 'node:path'
import { SitemapStream } from 'sitemap'
import { defineConfig } from 'vitepress'

const links = []

// ...
export default defineConfig({
  transformHtml: (_, id, { pageData }) => {
    if (!/[\\/]404\.html$/.test(id))
      links.push({
        // you might need to change this if not using clean urls mode
        url: pageData.relativePath.replace(/((^|\/)index)?\.md$/, '$2'),
        lastmod: pageData.lastUpdated
      })
  },
  buildEnd: async ({ outDir }) => {
    const sitemap = new SitemapStream({
      hostname: 'https://subwork.xyz/'
    })
    const writeStream = createWriteStream(resolve(outDir, 'sitemap.xml'))
    sitemap.pipe(writeStream)
    links.forEach((link) => sitemap.write(link))
    sitemap.end()
    await new Promise((r) => writeStream.on('finish', r))
  },

@Red-Asuka
Copy link

If you are not using clean URLs mode, you can write it like this:

  transformHtml: (_, id, { pageData }) => {
    if (!/[\\/]404\.html$/.test(id)) {
      links.push({
        url: pageData.relativePath.replace(/\/index\.md$/, '/').replace(/\.md$/, '.html'),
        lastmod: pageData.lastUpdated,
      })
    }
  },

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
build Related to the build system enhancement New feature or request has-workaround Has workaround, low priority
Projects
None yet
Development

Successfully merging a pull request may close this issue.