Skip to content

Commit

Permalink
feat: rehype plugins (#65)
Browse files Browse the repository at this point in the history
* feat: handle rehype plugins with recursive merging

BREAKING CHANGE: plugins disappear in favor of remarkPlugins and rehypePlugins. basePlugins also disappear in favor of deep merging and functions override

* docs: update for rehype plugins

* feat(lib): improve plugins parser

* docs: fix code-group component

* docs: update for new plugins options

* docs: add changelog page

* feat(lib): use nuxt resolver for local plugins

* docs: update configuration

* feat(lib): remove nuxt dependency

* docs: minor improvements

* chore: add getOptions for programmatic usage

* fix(lib): add missing property after merge

* chore(lib): improve plugins parsing

* test: options merging

Co-authored-by: Sébastien Chopin <[email protected]>
  • Loading branch information
benjamincanac and atinux authored Jun 10, 2020
1 parent ddd892c commit f6bb586
Show file tree
Hide file tree
Showing 9 changed files with 423 additions and 98 deletions.
10 changes: 4 additions & 6 deletions docs/components/bases/CodeGroup.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
<template>
<div class="code-group">
<div class="rounded-t border-b-2 border-gray-700 px-2 bg-gray-800 text-sm text-white relative ">
<div class="rounded-t border-b-2 border-gray-700 px-2 bg-gray-800 text-sm text-white relative">
<button
v-for="({ label }, i) in tabs"
ref="tabs"
:key="label"
class="px-4 py-3 text-gray-400 font-bold font-mono"
:class="[activeTabIndex === i && 'active']"
@click="updateTabs(i)"
>
{{ label }}
</button>
>{{ label }}</button>
<span ref="highlight-underline" class="highlight-underline" />
</div>
<slot />
Expand All @@ -31,7 +29,7 @@ export default {
}
},
created () {
this.$slots.default.map((slot) => {
this.$slots.default.filter(slot => Boolean(slot.componentOptions)).map((slot) => {
this.tabs.push({
label: slot.componentOptions.propsData.label,
elm: null
Expand All @@ -40,7 +38,7 @@ export default {
this.activeTabIndex = 0
},
mounted () {
this.tabs = this.$slots.default.map((slot) => {
this.tabs = this.$slots.default.filter(slot => Boolean(slot.componentOptions)).map((slot) => {
return {
label: slot.componentOptions.propsData.label,
elm: slot.elm
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ category: Getting started

```js
export default {
modules: [,
modules: [
'@nuxt/content'
],
generate: {
Expand Down
21 changes: 21 additions & 0 deletions docs/content/en/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
title: Changelog
description: 'Learn the changes made of the different versions of @nuxt/content module.'
position: 8
category: Getting started
---

## v2.0.0

### Markdown plugins

<base-alert>
markdown.basePlugins and markdown.plugins have been removed in favor of markdown.remarkPlugins and markdown.rehypePlugins.
</base-alert>

- Overriding plugins options can still be done but with prefixing by `remark` or `rehype`, for examps `externalLinks` becomes `remarkExternalLinks`.
- You can now override all plugins or append a new plugin using `remarkPlugins` / `rehypePlugins`.
- You can use local plugins using `~/plugins/remark-plugin.js`.
- You can register and configure plugins on the same line like `['remark-plugin', { option: 1 }]`.

Everything is documented in [this section](/configuration#markdown).
156 changes: 127 additions & 29 deletions docs/content/en/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ export default {
}
```

See [defaults options](#defaults).
Before diving into the individual attributes, please have a look [at the default settings](#defaults) of the module.

### Merging defaults

You can define every option either as function or as static value (primitives, objects, arrays, ...).
if you use a function, the default value will be provided as the first argument.

If you *don't* use a function to define you properties, the module will try to
merge them with the default values. This can be handy for `markdown.remarkPlugins`, `markdown.rehypePlugins` and so on because
the defaults are quite sensible. If you don't want to have the defaults include, just use a function.

## Properties

Expand Down Expand Up @@ -79,46 +88,128 @@ content: {

### `markdown`

This module uses [remark](https://github.com/remarkjs/remark) under the hood to compile markdown files into JSON AST that will be stored into the `body` variable.
This module uses [remark](https://github.com/remarkjs/remark) and [rehype](https://github.com/rehypejs/rehype) under the hood to compile markdown files into JSON AST that will be stored into the `body` variable.

By default, this module uses plugins to improve markdown parsing. You can add your own by using `plugins` or override the default ones by using `basePlugins`. Each plugin is configured using its name in camelCase: `remark-external-links` => `externalLinks`.
<base-alert type="info">
The following explanation is valid for both `remarkPlugins` and `rehypePlugins`
</base-alert>

> You check for remark plugins [here](https://github.com/remarkjs/remark/blob/master/doc/plugins.md#list-of-plugins)
To configure how the module will parse Markdown, you can:

### `markdown.basePlugins`
- Add a new plugin to the defaults:

- Type: `Array`
- Default: `['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes']`
```js{}[nuxt.config.js]
export default {
content: {
markdown: {
remarkPlugins: ['remark-emoji']
}
}
}
```

### `markdown.plugins`
- Override the default plugins:

- Type: `Array`
- Default: `[]`
```js{}[nuxt.config.js]
export default {
content: {
markdown: {
remarkPlugins: () => ['remark-emoji']
}
}
}
```

### `markdown.externalLinks`
- Use local plugins

- Type: `Object`
- Default: `{}`
```js{}[nuxt.config.js]
export default {
content: {
markdown: {
remarkPlugins: [
'~/plugins/my-custom-remark-plugin.js'
]
}
}
}
```

You can control the behaviour of links via this option. You can check here for [options](https://github.com/remarkjs/remark-external-links#api).
- Provide options directly in the definition

```js{}[nuxt.config.js]
content: {
markdown: {
externalLinks: {
target: '_self' // disable target="_blank"
rel: false // disable rel="nofollow noopener"
export default {
content: {
markdown: {
remarkPlugins: [
['remark-emoji', { emoticon: true }]
]
}
}
}
```

### `markdown.footnotes`
- Provide options using the name of the plugin in `camelCase`

- Type: `Object`
- Default: `{ inlineNotes: true }`
```js{}[nuxt.config.js]
export default {
content: {
markdown: {
// https://github.com/remarkjs/remark-external-links#options
remarkExternalLinks: {
target: '_self',
rel: 'nofollow'
}
}
}
}
```

<base-alert>
When adding a new plugin, make sure to install it in your dependencies:
</base-alert>

<code-group>
<code-block label="Yarn" active>

```bash
yarn add remark-emoji
```

</code-block>
<code-block label="NPM">

```bash
npm install remark-emoji
```

</code-block>
</code-group>

```js{}[nuxt.config.js]
export default {
content: {
markdown: {
remarkPlugins: ['remark-emoji']
}
}
}
```

### `markdown.remarkPlugins`

- Type: `Array`
- Default: `['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes']`
- Version: **v2.0.0**

> You can take a look at the list of [remark plugins](https://github.com/remarkjs/remark/blob/master/doc/plugins.md#list-of-plugins).
### `markdown.rehypePlugins`

- Type: `Array`
- Default: `['rehype-minify-whitespace', 'rehype-sort-attribute-values', 'rehype-sort-attributes', 'rehype-raw']`
- Version: **v2.0.0**

You can control the behaviour of footnotes via this option. You can check here for [options](https://github.com/remarkjs/remark-footnotes#remarkusefootnotes-options).
> You can take a look at the list of [rehype plugins](https://github.com/rehypejs/rehype/blob/master/doc/plugins.md#list-of-plugins).
### `markdown.prism.theme`

Expand Down Expand Up @@ -177,12 +268,19 @@ export default {
fullTextSearchFields: ['title', 'description', 'slug', 'text'],
nestedProperties: [],
markdown: {
externalLinks: {},
footnotes: {
inlineNotes: true
},
basePlugins: ['remark-squeeze-paragraphs', 'remark-slug', 'remark-autolink-headings', 'remark-external-links', 'remark-footnotes'],
plugins: [],
remarkPlugins: [
'remark-squeeze-paragraphs',
'remark-slug',
'remark-autolink-headings',
'remark-external-links',
'remark-footnotes'
],
rehypePlugins: [
'rehype-minify-whitespace',
'rehype-sort-attribute-values',
'rehype-sort-attributes',
'rehype-raw'
],
prism: {
theme: 'prismjs/themes/prism.css'
}
Expand Down
8 changes: 5 additions & 3 deletions docs/content/en/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ It will be transformed to it's JSON AST structure, and by using the `nuxt-conten
```

> The links in headings are empty and therefore hidden, so it's up to you to style them. For an example, try hovering one of the headers in these docs.
### Links

Links are transformed to add valid `target` and `rel` attributes. You can change this behaviour, see [configuration](/configuration#markdownexternallinks). Relative links are also automatically transformed to `nuxt-link` to provide navigation between page components with enhanced performance through smart prefetching.
Links are transformed to add valid `target` and `rel` attributes using [remark-external-links](https://github.com/remarkjs/remark-external-links). You can check [here](/configuration#markdown) to learn how to configure this plugin.

Relative links are also automatically transformed to [nuxt-link](https://nuxtjs.org/api/components-nuxt-link/) to provide navigation between page components with enhanced performance through smart prefetching.

Here is an example using external, relative, markdown and html links:

Expand All @@ -116,7 +118,7 @@ title: Home

### Footnotes

This module supports extended markdown syntax for footnotes.
This module supports extended markdown syntax for footnotes using [remark-footnotes](https://github.com/remarkjs/remark-footnotes). You can check [here](/configuration#markdown) to learn how to configure this plugin.

Here is an example using footnotes:

Expand Down
59 changes: 21 additions & 38 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,23 @@ const defu = require('defu')
const middleware = require('./middleware')
const Database = require('./database')
const WS = require('./ws')
const { getDefaults, mergeConfig, processMarkdownOptions } = require('./utils')

const defaults = {
watch: false,
apiPrefix: '_content',
dir: 'content',
fullTextSearchFields: ['title', 'description', 'slug', 'text'],
nestedProperties: [],
markdown: {
basePlugins: [
'remark-squeeze-paragraphs',
'remark-slug',
'remark-autolink-headings',
'remark-external-links',
'remark-footnotes'
],
plugins: [
],
footnotes: {
inlineNotes: true
},
externalLinks: {},
prism: {
theme: 'prismjs/themes/prism.css'
}
},
yaml: {},
csv: {}
}
module.exports = async function (moduleOptions) {
const isSSG = this.options.dev === false && (this.options.target === 'static' || this.options._generate)

module.exports = async function () {
const isSSG =
this.options.dev === false &&
(this.options.target === 'static' || this.options._generate)
const options = defu(
{
watch: this.options.dev,
...this.options.content
},
defaults
)
const { content = {} } = Object.assign({}, this.options)
Object.assign(content, moduleOptions)

const defaults = getDefaults({ dev: this.options.dev })

const mergedConfig = mergeConfig(content, defaults)
const options = defu(mergedConfig, defaults)
options.dir = resolve(this.options.srcDir, options.dir)

// Load markdown plugins
processMarkdownOptions(options, this.nuxt.resolver.resolvePath)

options.apiPrefixWithBase = options.apiPrefix
if (this.options.router.base) {
let baseRouter = this.options.router.base
Expand Down Expand Up @@ -197,3 +171,12 @@ module.exports = async function () {

module.exports.Database = Database
module.exports.middleware = middleware
module.exports.getOptions = (userOptions = {}) => {
const defaults = getDefaults({ dev: process.env.NODE_ENV !== 'production' })
const mergedConfig = mergeConfig(userOptions, defaults)
const options = defu(mergedConfig, defaults)

processMarkdownOptions(options)

return options
}
Loading

0 comments on commit f6bb586

Please sign in to comment.