Skip to content

Commit

Permalink
[MDX] Support remark and rehype plugins, with defaults (#3977)
Browse files Browse the repository at this point in the history
* feaet: allow remark and rehype plugin config

* deps: add remark-gfm, remark-smartypants

* feat: add gfm and smartypants by default

* test: add GFM and remark plugin tests

* feat: preserve default plugins with "extends"

* docs: add remarkPlugins

* docs: add rehypePlugins

* chore: changeset

* fix: remove skip from mdx tests

* chore: dup hyperlink flavor text

* chore: authGen -> autoGen

* nit: markdown -> Markdown

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

* nit: markdown -> Markdown 1

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

* nit: markdown -> Markdown 2

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

* nit: markdown -> Markdown 3

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

* nit: markdown -> Markdown 4

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
  • Loading branch information
bholmesdev and natemoo-re authored Jul 20, 2022
1 parent d50f46b commit 19433eb
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-coats-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/mdx': minor
---

Add remarkPlugins and rehypePlugins to config, with the same default plugins as our standard Markdown parser
60 changes: 59 additions & 1 deletion packages/integrations/mdx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,65 @@ Also check our [Astro Integration Documentation][astro-integration] for more on

## Configuration

There are currently no configuration options for the `@astrojs/mdx` integration. Please [open an issue](https://github.com/withastro/astro/issues/new/choose) if you have a compelling use case to share.
<details>
<summary><strong>remarkPlugins</strong></summary>

**Default plugins:** [remark-gfm](https://github.com/remarkjs/remark-gfm), [remark-smartypants](https://github.com/silvenon/remark-smartypants)

[Remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) allow you to extend your Markdown with new capabilities. This includes [auto-generating a table of contents](https://github.com/remarkjs/remark-toc), [applying accessible emoji labels](https://github.com/florianeckerstorfer/remark-a11y-emoji), and more. We encourage you to browse [awesome-remark](https://github.com/remarkjs/awesome-remark) for a full curated list!

We apply [GitHub-flavored Markdown](https://github.com/remarkjs/remark-gfm) and [Smartypants](https://github.com/silvenon/remark-smartypants) by default. This brings some niceties like auto-generating clickable links from text (ex. `https://example.com`) and formatting quotes for readability. When applying your own plugins, you can choose to preserve or remove these defaults.

To apply plugins _while preserving_ Astro's default plugins, use a nested `extends` object like so:

```js
// astro.config.mjs
import remarkToc from 'remark-toc';

export default {
integrations: [mdx({
// apply remark-toc alongside GitHub-flavored markdown and Smartypants
remarkPlugins: { extends: [remarkToc] },
})],
}
```

To apply plugins _without_ Astro's defaults, you can apply a plain array:

```js
// astro.config.mjs
import remarkToc from 'remark-toc';

export default {
integrations: [mdx({
// apply remark-toc alone, removing other defaults
remarkPlugins: [remarkToc],
})],
}
```

</details>

<details>
<summary><strong>rehypePlugins</strong></summary>

**Default plugins:** none

[Rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md) allow you to transform the HTML that your Markdown generates. We recommend checking the [Remark plugin](https://github.com/remarkjs/remark/blob/main/doc/plugins.md) catalog first _before_ considering rehype plugins, since most users want to transform their Markdown syntax instead. If HTML transforms are what you need, we encourage you to browse [awesome-rehype](https://github.com/rehypejs/awesome-rehype) for a full curated list of plugins!

To apply rehype plugins, use the `rehypePlugins` configuration option like so:

```js
// astro.config.mjs
import rehypeMinifyHtml from 'rehype-minify';

export default {
integrations: [mdx({
rehypePlugins: [rehypeMinifyHtml],
})],
}
```
</details>

## Examples

Expand Down
7 changes: 5 additions & 2 deletions packages/integrations/mdx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
},
"dependencies": {
"@mdx-js/rollup": "^2.1.1",
"es-module-lexer": "^0.10.5"
"es-module-lexer": "^0.10.5",
"remark-gfm": "^3.0.1",
"remark-smartypants": "^2.0.0"
},
"devDependencies": {
"@types/chai": "^4.3.1",
Expand All @@ -41,7 +43,8 @@
"astro-scripts": "workspace:*",
"chai": "^4.3.6",
"linkedom": "^0.14.12",
"mocha": "^9.2.2"
"mocha": "^9.2.2",
"remark-toc": "^8.0.1"
},
"engines": {
"node": "^14.18.0 || >=16.12.0"
Expand Down
24 changes: 22 additions & 2 deletions packages/integrations/mdx/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import mdxPlugin from '@mdx-js/rollup';
import mdxPlugin, { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
import type { AstroIntegration } from 'astro';
import { parse as parseESM } from 'es-module-lexer';
import remarkGfm from 'remark-gfm';
import remarkSmartypants from 'remark-smartypants';
import { getFileInfo } from './utils.js';

export default function mdx(): AstroIntegration {
type WithExtends<T> = T | { extends: T };

type MdxOptions = {
remarkPlugins?: WithExtends<MdxRollupPluginOptions['remarkPlugins']>;
rehypePlugins?: WithExtends<MdxRollupPluginOptions['rehypePlugins']>;
}

const DEFAULT_REMARK_PLUGINS = [remarkGfm, remarkSmartypants];

function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] = []): T[] | undefined {
if (Array.isArray(config)) return config;

return [...defaults, ...(config?.extends ?? [])];
}

export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
return {
name: '@astrojs/mdx',
hooks: {
Expand All @@ -15,6 +32,9 @@ export default function mdx(): AstroIntegration {
{
enforce: 'pre',
...mdxPlugin({
remarkPlugins: handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS),
rehypePlugins: handleExtends(mdxOptions.rehypePlugins),
// place these after so the user can't override
jsx: true,
jsxImportSource: 'astro',
// Note: disable `.md` support
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# GitHub-flavored Markdown test

This should auto-gen a link: https://example.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# TOC test

## Table of contents

## Section 1

Some text!

### Subsection 1

Some subsection test!

### Subsection 2

Oh cool, more text!

## Section 2

And section 2, with a hyperlink to check GFM is preserved: https://handle-me-gfm.com
58 changes: 58 additions & 0 deletions packages/integrations/mdx/test/mdx-remark-plugins.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import mdx from '@astrojs/mdx';

import { expect } from 'chai';
import { parseHTML } from 'linkedom';
import { loadFixture } from '../../../astro/test/test-utils.js';
import remarkToc from 'remark-toc';

const FIXTURE_ROOT = new URL('./fixtures/mdx-remark-plugins/', import.meta.url);

describe('MDX remark plugins', () => {
it('supports custom remark plugins - TOC', async () => {
const fixture = await loadFixture({
root: FIXTURE_ROOT,
integrations: [mdx({
remarkPlugins: [remarkToc],
})],
});
await fixture.build();

const html = await fixture.readFile('/with-toc/index.html');
const { document } = parseHTML(html);

const tocLink1 = document.querySelector('ul a[href="#section-1"]');
expect(tocLink1).to.not.be.null;
});

it('applies GitHub-flavored markdown by default', async () => {
const fixture = await loadFixture({
root: FIXTURE_ROOT,
integrations: [mdx()],
});
await fixture.build();

const html = await fixture.readFile('/with-gfm/index.html');
const { document } = parseHTML(html);

const autoGenLink = document.querySelector('a[href="https://example.com"]');
expect(autoGenLink).to.not.be.null;
});

it('preserves default GitHub-flavored markdown with "extends"', async () => {
const fixture = await loadFixture({
root: FIXTURE_ROOT,
integrations: [mdx({
remarkPlugins: { extends: [remarkToc] },
})],
});
await fixture.build();

const html = await fixture.readFile('/with-toc/index.html');
const { document } = parseHTML(html);

const tocLink1 = document.querySelector('ul a[href="#section-1"]');
expect(tocLink1).to.not.be.null;
const autoGenLink = document.querySelector('a[href="https://handle-me-gfm.com"]');
expect(autoGenLink).to.not.be.null;
});
});
47 changes: 46 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 19433eb

Please sign in to comment.