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

feat(content): pass document data to remark plugins #782

Merged
merged 4 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions docs/content/en/snippets.md
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,81 @@ export default {
}
}
```

### Remark Plugin

Nuxt Content used [remark](https://github.com/remarkjs/remark) under the hood to process markdown documents. Creating remark plugins is a way to manipulate document and add new features contents.

#### List all contributors

Let's say you want to list all contributors of the project in a document. You can create a plugin that fetches all contributors and injects them into document data.

- Create the plugin, this plugin fetches the contributors if `fetchContributors` is set to `true`

```js [~~/plugins/contributors.js]
const fetch = require('node-fetch')

module.exports = function () {
return async (tree, { data }) => {
if (data.fetchContributors) {
const contributors = await fetch(
'https://api.github.com/repos/nuxt/content/contributors'
).then(res => res.json())
.then(res => res.map(({ login }) => login))

data.$contributors = [...new Set(contributors)]
}
return tree
}
}
```

- Register plugin in Nuxt config

```js{}[nuxt.config.js]
export default {
contents: {
markdown: {
remarkPlugins: [
'~~/plugins/contributors.js'
]
}
}
}
```

- Create a simple component to show contributors

```vue{}[~~/components/List.vue]
<template>
<ul>
<li v-for="(item, i) in items" :key="i">
{{ item }}
</li>
</ul>
</template>

<script>
export default {
props: {
items: {
type: Array,
default: () => []
}
}
}
```

- Finally use the components and mark document to fetch the contributors

```md{}[document.md]
---
title: Nuxt Content
fetchContributors: true
---

## Contributors

<list :items="$contributors"></list>

```
16 changes: 16 additions & 0 deletions example/components/global/Authors.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</template>

<script>
export default {
props: {
items: {
type: Array,
default: () => []
}
}
}
</script>
9 changes: 9 additions & 0 deletions example/content/authors-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
title: Auto generated authors list

---

Authors list:


<authors />
5 changes: 5 additions & 0 deletions example/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const config: NuxtConfig = {
nestedProperties: ['categories.slug'],
extendParser: {
'.custom': file => ({ body: file.split('\n').map(line => line.trim()) })
},
markdown: {
remarkPlugins: [
'~/utils/contributors'
]
}
}
};
Expand Down
16 changes: 16 additions & 0 deletions example/pages/authors.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<div>
<nuxt-content :document="markdown" />
</div>
</template>

<script lang="ts">
export default {
async asyncData ({ $content }) {
const markdown = await $content('authors-page').fetch()
return {
markdown
}
}
}
</script>
26 changes: 26 additions & 0 deletions example/utils/contributors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const fetch = require('node-fetch')

module.exports = function () {
return async (tree, file) => {
let filePath
tree.children = tree.children.map((node) => {
const TAG_REGEX = /\s*<(authors)\s*/i
if (node.type === 'html' && node.value.match(TAG_REGEX)) {
filePath = 'README.md' // detect file path
node.value = node.value.replace(TAG_REGEX, '<$1 :items="$authors" ')
}
return node
})
if (filePath) {
const commits = await fetch(
'https://api.github.com/repos/nuxt/content/commits?path=' + filePath
).then(res => res.json())
const authors = commits
.map(commit => commit.author.login)
.filter(Boolean)

file.data.$authors = [...new Set(authors)]
}
return tree
}
}
10 changes: 6 additions & 4 deletions packages/content/parsers/markdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ class Markdown {
/**
* Generate json body
* @param {string} content - JSON AST generated from markdown.
* @param {object} data - document data
* @returns {object} JSON AST body
*/
async generateBody (content) {
async generateBody (content, data = {}) {
let { highlighter } = this.options
if (typeof highlighter === 'function' && highlighter.length === 0) {
highlighter = await highlighter()
Expand All @@ -93,7 +94,7 @@ class Markdown {

stream
.use(jsonCompiler)
.process(content, (error, file) => {
.process({ data, contents: content }, (error, file) => {
/* istanbul ignore if */
if (error) {
return reject(error)
Expand All @@ -120,8 +121,9 @@ class Markdown {
async toJSON (file) {
const { data, content, ...rest } = matter(file, { excerpt: true, excerpt_separator: '<!--more-->' })

const documentData = data || {}
// Compile markdown from file content to JSON
const body = await this.generateBody(content)
const body = await this.generateBody(content, documentData)
// Generate toc from body
const toc = this.generateToc(body)

Expand All @@ -134,7 +136,7 @@ class Markdown {

return {
description,
...data,
...documentData,
toc,
body,
text: content,
Expand Down