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

Metadata for collections of pages (blogs etc.) #96

Closed
petedavisdev opened this issue Oct 11, 2020 · 15 comments
Closed

Metadata for collections of pages (blogs etc.) #96

petedavisdev opened this issue Oct 11, 2020 · 15 comments
Labels
enhancement New feature or request keep-open

Comments

@petedavisdev
Copy link

petedavisdev commented Oct 11, 2020

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
Copy link
Author

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.

@kiaking
Copy link
Member

kiaking commented Nov 5, 2020

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.

@petedavisdev
Copy link
Author

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 petedavisdev changed the title Metadata for collections of pages Metadata for collections of pages (blogs etc.) Nov 7, 2020
@jivane-perich

This comment was marked as off-topic.

@brc-dd brc-dd mentioned this issue Jun 1, 2022
4 tasks
@brc-dd
Copy link
Member

brc-dd commented Jun 4, 2022

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

@staghouse
Copy link

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.

@Charles7c
Copy link
Contributor

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

@zRains
Copy link
Contributor

zRains commented Aug 15, 2022

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

@brc-dd
Copy link
Member

brc-dd commented Aug 15, 2022

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.

@staghouse
Copy link

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.

@kiaking
Copy link
Member

kiaking commented Mar 8, 2023

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

@brc-dd
Copy link
Member

brc-dd commented Mar 10, 2023

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 brc-dd closed this as not planned Won't fix, can't repro, duplicate, stale Mar 10, 2023
@Charles7c
Copy link
Contributor

// 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

@brc-dd
Copy link
Member

brc-dd commented Mar 12, 2023

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

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

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 20, 2023
@vuejs vuejs unlocked this conversation Mar 20, 2023
@brc-dd
Copy link
Member

brc-dd commented May 8, 2023

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 brc-dd closed this as completed May 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request keep-open
Projects
None yet
Development

No branches or pull requests

7 participants