vitepress icon indicating copy to clipboard operation
vitepress copied to clipboard

Metadata for collections of pages (blogs etc.)

Open petedavisdev opened this issue 4 years ago • 9 comments

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

I would like to be able to display a collection of pages (e.g. my latest blog posts or an alphabetized list of API docs). VuePress has $site.pages, but VitePress does not ship metadata about all pages, which is great except when I need that data for certain pages.

Describe the solution you'd like

I would like to specify collections of pages that I need metadata for in config.js, for example:

module.exports = {
  title: "My Tech Blog",
  collections: [
    {
      name: 'blog',
      directory: '_posts/',
      layout: 'BlogPostLayout',
    },
    {
      name: 'api',
      directory: 'guide/api/',
      layout: 'TechDocLayout',
    }
  ],
};

This would produce an array of metadata for pages inside the _posts directory could then be accessed via $site.collections.blog

I've also included a layout option that could be used to define a default layout for pages in that collection. That's a separate idea, but the point is that collections could have additional benefits.

You could possibly specify which metadata you need - e.g. you may or may not need the frontmatter for every page in the collection.

I've borrowed the term "collections" from NetlifyCMS, which I use with VuePress currently.

Describe alternatives you've considered

Alternatively, you could simply have a config option to ship metadata for all pages, but that would be all or nothing.

Additional context

Here's an example of how I've implemented collections in a VuePress theme: themeConfig.js, PostList.vue, GlobalLayout.vue

petedavisdev avatar Oct 11 '20 16:10 petedavisdev

To keep it lightweight I'd suggest collections are shallow - only include metadata for .md files that are directly inside the specified folder. If you wanted a subfolder, that would be another collection.

Also to keep it light, the inclusion of frontmatter in the metadata could be optional.

petedavisdev avatar Oct 13 '20 14:10 petedavisdev

Currently, VitePress is designed to be very small, and opinionated. While this feature sounds nice, I think this feature is more toward blogging. We're currently not sure if we want VitePress to have these kind of feature.

I think we need to wait until we really decide the key difference between VuePress and VitePress.

kiaking avatar Nov 05 '20 07:11 kiaking

That makes sense for now - I love how minimalist VitePress is.

I do think this will be a common request. Even if the primary purpose of VitePress is for documentation sites, it's common for people to add a simple blog or some other kind of page listing to their docs site.

I might have a play and see if I can get this working, just as an example of a possible implementation.

petedavisdev avatar Nov 05 '20 14:11 petedavisdev

Seems possible with a customMetadata (as shown here in vuejs blog : https://github.com/vuejs/blog/blob/master/.vitepress/config.js )

jivane-perich avatar Jan 21 '21 11:01 jivane-perich

I just tested writing a blog with VitePress. It was quite easy actually. I was able to write a short script that generated a json file for all articles:

import fs from 'node:fs/promises'
import matter from 'gray-matter'
import removeMd from 'remove-markdown'

const articles = await fs.readdir('./blog/')

const data = await Promise.all(
  articles.map(async (article) => {
    const file = matter.read(`./blog/${article}`, {
      excerpt: true,
      excerpt_separator: '<!-- more -->'
    })

    const { data, excerpt, path } = file
    const contents = removeMd(excerpt).trim().split(/\r\n|\n|\r/)

    return {
      ...data,
      title: contents[0].replace(/\s{2,}/g, '').trim(),
      path: path.replace(/\.md$/, '.html'),
      excerpt: contents.slice(1).join('').replace(/\s{2,}/g, '').trim()
    }
  })
)

await fs.writeFile('./data.json', JSON.stringify(data), 'utf-8')

Here is the complete repo: https://github.com/brc-dd/vitepress-blog-demo

Final result (didn't do any styling):

image

I guess this feature is less likely to be supported officially. It will be better if someone can write a Vite plugin to auto-generate that data before build.

Something like this can also be done: https://github.com/vuejs/blog

brc-dd avatar Jun 04 '22 19:06 brc-dd

Has there been any more discussion about wether or not to include something like this.$site.pages from VuePress in to VitePress? I understand VitePress should be light weight but if the core belief is to provide a SSG website based on markdown then to me it makes sense to provide some sort of leveraging in to all data of the generated pages. The more relevant data the better IMO.

staghouse avatar Jul 21 '22 01:07 staghouse

import fs from 'node:fs/promises'
import matter from 'gray-matter'
import removeMd from 'remove-markdown'

const articles = await fs.readdir('./blog/')

const data = await Promise.all(
  articles.map(async (article) => {
    const file = matter.read(`./blog/${article}`, {
      excerpt: true,
      excerpt_separator: '<!-- more -->'
    })

    const { data, excerpt, path } = file
    const contents = removeMd(excerpt).trim().split(/\r\n|\n|\r/)

    return {
      ...data,
      title: contents[0].replace(/\s{2,}/g, '').trim(),
      path: path.replace(/\.md$/, '.html'),
      excerpt: contents.slice(1).join('').replace(/\s{2,}/g, '').trim()
    }
  })
)

await fs.writeFile('./data.json', JSON.stringify(data), 'utf-8')

thanks.

image image

Charles7c avatar Aug 03 '22 15:08 Charles7c

@brc-dd Is it possible to get all page info in distributed theme?

zRains avatar Aug 15 '22 09:08 zRains

Is it possible to get all page info in distributed theme?

@zRains there is no direct way at the moment. The alternatives I had mentioned would work. New transformHTML/buildEnd hooks might be helpful too.

brc-dd avatar Aug 15 '22 10:08 brc-dd

While I have found work around with custom scripts to create page data I think VitePress needs to make a stand about wether or not its going be support site level page data like VuePress has, or not. I understand we have these hooks to help us now but there's not documentation examples on really how to use these hooks to do anything meaningful.

I'd like to see some updated docs for examples of getting all page data if VitePress is really never going to return a collection of pages meta data.

staghouse avatar Mar 04 '23 14:03 staghouse

Now we have Build-Time Data Loading. Would this solve this issue?

kiaking avatar Mar 08 '23 09:03 kiaking

Refer the example given here: https://vitepress.dev/guide/data-loading#data-from-local-files

We won't be providing data from all the pages like VuePress does.

brc-dd avatar Mar 10 '23 14:03 brc-dd

// article.data.js
import fs from 'node:fs';
import path from 'node:path';
import parseFrontmatter from 'gray-matter';

const excludedFiles = ['index.md', 'tags.md', 'archives.md', 'me.md'];

export default {
  watch: ['./docs/**/*.md'],
  load(watchedFiles) {
    // 排除不必要文件
    const articleFiles = watchedFiles.filter(file => {
      const filename = path.basename(file);
      return !excludedFiles.includes(filename);
    });
    // 解析文章 Frontmatter
    return articleFiles.map(articleFile => {
      const articleContent = fs.readFileSync(articleFile, 'utf-8');
      const { data } = parseFrontmatter(articleContent);
      return {
        ...data,
        path: articleFile.substring(articleFile.lastIndexOf('/docs/') + 6).replace(/\.md$/, ''),
      }
    })
  }
}

How to exclude unnecessary files in watch? @brc-dd

Charles7c avatar Mar 12 '23 07:03 Charles7c

@Charles7c Something like this should work (might need to slightly adjust paths):

  watch: ['./**/*.md', `!${__dirname}/docs/{index,tags,archives,me}.md`],

brc-dd avatar Mar 12 '23 07:03 brc-dd

We also provide a createContentLoader helper: https://vitepress.dev/guide/data-loading#createcontentloader

So, now you don't need to do much custom stuff, just a simple export and optionally some configuration would do the job!

brc-dd avatar May 08 '23 16:05 brc-dd