Search code examples
vue.jsnuxt.jschangelog

Nuxt / Vue JS - Writing a HTML template for a markdown file


I have a Markdown file which contains release notes. For example:

## 1.0.0

### New
#### Added new language support

### Fixed
#### This is fixed
#### This is fixed

### Changed
#### Something changed

### Feature
#### New Feature Added

## 2.0.0

### New
#### Added new language support

In my HTML, I would like loop over the release notes for all new releases. I have queried the document in the page like so:

<template>
    <changelog :article="article"/> 
</template>

<script lang="ts">
  import Vue from "vue";
  export default Vue.extend({
    async asyncData({ $content }) {
      const article = await $content("changelogs/iOS/changelog").fetch();
      return { article };
    },  
    watchQuery: true,
  });
</script>

In my component, I have:

<template>
    <article :document="article">
</template>

I would like the result to be something like this: enter image description here


Solution

  • The parse result of the content is stored in article.body.children[]. Each child contains the following node data:

    • tag - HTML tag (e.g., h2, h3)
    • type - Element type (e.g., element, text)
    • value - Element value (the text contents)
    • props - Extra props data, including id
    • children[] - Child nodes

    You could use that info to parse the nodes into a convenient data structure that stores release info, such as:

    • title from the text child of h2
    • id from props of h2
    • changes[] to hold the change lines, each containing:
      • id from props of h4
      • type from the text child of h3
      • text from the text child of h4
    const elems = article.body.children.filter(node => node.type === 'element')
    let rel = {}
    let type = ''
    for (const node of elems) {
      if (node.tag === 'h2') {
        rel = {
          id: node.props.id,
          title: node.children.find(c => c.type === 'text').value,
          changes: []
        }
        this.releases.push(rel)
    
      } else if (node.tag === 'h3') {
        type = node.children.find(c => c.type === 'text').value
    
      } else if (node.tag === 'h4') {
        rel.changes.push({
          id: node.props.id,
          type,
          text: node.children.find(c => c.type === 'text').value,
        })
      }
    }
    

    Then, use v-for to render the list of release info parsed above.

    <article v-for="rel of releases" :key="rel.id">
      <h3 class="title">{{ rel.title }}</h3>
      <section class="change" v-for="change in rel.changes" :key="change.id">
        <div class="type" :class="change.type">{{ change.type }}</div>
        <div class="text">{{ change.text }}</div>
      </section>
    </article>
    

    demo