Skip to content

Commit d0b4b9d

Browse files
authored
mdx-v2: feat: add support for remark, rehype and gatsby-remark plugins (#35751)
* feat: add support for remark, rehype and gatsby-remark plugins * clean up * refactor: extract our two custom remark into their own files * fix: pass path to MDX file for remark plugins * refactor: remove type assertions * feat: extend plugin option validation * remove old comment
1 parent 59f1300 commit d0b4b9d

17 files changed

+630
-136
lines changed

e2e-tests/mdx/gatsby-config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@ module.exports = {
1818
},
1919
},
2020
`gatsby-plugin-test-regression1`,
21+
`gatsby-plugin-sharp`,
2122
{
2223
resolve: `gatsby-plugin-mdx`,
2324
options: {
24-
lessBabel: false,
2525
extensions: [`.mdx`, `.md`],
2626
defaultLayouts: {
2727
default: require.resolve("./src/components/layout.js"),
2828
},
29-
remarkPlugins: [remarkRequireFilePathPlugin],
29+
gatsbyRemarkPlugins: [`gatsby-remark-images`],
30+
mdxOptions: {
31+
remarkPlugins: [remarkRequireFilePathPlugin],
32+
},
3033
},
3134
},
3235
!process.env.CI && `gatsby-plugin-webpack-bundle-analyser-v2`,

e2e-tests/mdx/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
"fs-extra": "^8.1.0",
99
"gatsby": "^4.0.0",
1010
"gatsby-plugin-mdx": "^3.0.0",
11+
"gatsby-plugin-sharp": "^4.0.0",
1112
"gatsby-plugin-webpack-bundle-analyser-v2": "^1.1.27",
13+
"gatsby-remark-images": "^6.15.0",
1214
"gatsby-source-filesystem": "^4.0.0",
1315
"react": "^17.0.2",
1416
"react-dom": "^17.0.2",
5.9 KB
Loading

e2e-tests/mdx/src/pages/index.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ Let's go MDX!
1010
const codefence = true
1111
```
1212

13-
<Example/>
13+
<Example />
1414

15-
<Message data-testid="external">Now an external import</Message>
15+
<Message data-testid="external">Now an external import</Message>
16+
17+
### An image:
18+
19+
![Alt text here](./gatsby-logo.png)
20+
21+
### HTML
22+
23+
<div data-something="important-information">
24+
<strong>This is a test</strong>
25+
</div>

packages/gatsby-plugin-mdx/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,17 @@
4949
"testEnvironment": "node"
5050
},
5151
"dependencies": {
52-
"@mdx-js/loader": "^2.1.1",
5352
"@mdx-js/mdx": "^2.1.1",
5453
"change-case": "^4.1.2",
54+
"deepmerge": "^4.2.2",
5555
"fs-extra": "^10.1.0",
56+
"gatsby-plugin-utils": "^3.9.0-next.2",
5657
"gray-matter": "^4.0.3",
57-
"mdast-util-from-markdown": "^1.2.0",
5858
"mdast-util-mdx": "^2.0.0",
5959
"mdast-util-to-markdown": "^1.3.0",
60-
"micromark-extension-mdxjs": "^1.0.0"
60+
"unified": "^10.1.2",
61+
"unist-util-visit": "^4.1.0",
62+
"vfile": "^5.3.2"
6163
},
6264
"devDependencies": {
6365
"babel-preset-gatsby-package": "2.16.0-next.0",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { ProcessorOptions } from "@mdx-js/mdx"
2+
import type { IFileNode, IMdxNode } from "./types"
3+
4+
// Compiles MDX into JS
5+
// This could be replaced by @mdx-js/mdx if MDX compile would
6+
// accept custom data passed to the unified pipeline via processor.data()
7+
export default async function compileMDX(
8+
source: string,
9+
mdxNode: IMdxNode,
10+
fileNode: IFileNode,
11+
options: ProcessorOptions
12+
): Promise<string> {
13+
const { createProcessor } = await import(`@mdx-js/mdx`)
14+
const { VFile } = await import(`vfile`)
15+
16+
const processor = createProcessor(options)
17+
18+
// If we could pass this via MDX loader config, this whole custom loader might be obsolete.
19+
processor.data(`mdxNode`, mdxNode)
20+
21+
const result = await processor.process(
22+
// Inject path to original file for remark plugins. See: https://github.com/gatsbyjs/gatsby/issues/26914
23+
new VFile({ value: source, path: fileNode.absolutePath })
24+
)
25+
26+
return result.toString()
27+
}

packages/gatsby-plugin-mdx/src/mdx-loader.ts renamed to packages/gatsby-plugin-mdx/src/gatsby-layout-loader.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,34 @@
11
/* eslint-disable @babel/no-invalid-this */
2-
import type { Node } from "gatsby"
32
import type { LoaderDefinition } from "webpack"
3+
import type { Options } from "mdast-util-to-markdown"
4+
import type { NodeMap } from "./types"
5+
import type { IMdxPluginOptions } from "./plugin-options"
46

57
import grayMatter from "gray-matter"
68
import { getOptions } from "loader-utils"
7-
import type { Options } from "mdast-util-to-markdown"
8-
9-
import { defaultOptions, IMdxPluginOptions } from "./plugin-options"
109

11-
interface IFileNode extends Node {
12-
sourceInstanceName: string
10+
export interface IGatsbyLayoutLoaderOptions {
11+
options: IMdxPluginOptions
12+
nodeMap: NodeMap
1313
}
1414

15-
export interface IMdxLoaderOptions {
16-
pluginOptions: IMdxPluginOptions
17-
fileMap: Map<string, IFileNode>
18-
}
19-
20-
// Wrap MDX content with Gatsby Layout component and inject components from `@mdx-js/react`
21-
const gatsbyMdxLoader: LoaderDefinition = async function (source) {
22-
const { pluginOptions, fileMap }: IMdxLoaderOptions = getOptions(this)
23-
24-
const options = defaultOptions(pluginOptions)
15+
// Wrap MDX content with Gatsby Layout component
16+
const gatsbyLayoutLoader: LoaderDefinition = async function (source) {
17+
const { options, nodeMap }: IGatsbyLayoutLoaderOptions = getOptions(this)
2518

26-
const fileNode = fileMap.get(this.resourcePath)
19+
const res = nodeMap.get(this.resourcePath)
2720

28-
if (!fileNode) {
21+
if (!res) {
2922
throw new Error(
3023
`Unable to locate GraphQL File node for ${this.resourcePath}`
3124
)
3225
}
3326

34-
// get the default layout for the file source group, or if it doesn't
35-
// exist, the overall default layout
36-
const layoutPath =
37-
options.defaultLayouts[fileNode.sourceInstanceName] ||
38-
options.defaultLayouts[`default`]
27+
// Get the default layout for the file source instance group name,
28+
// or fall back to the default
29+
const layoutPath = res.fileNode.sourceInstanceName
30+
? options.defaultLayouts[res.fileNode.sourceInstanceName]
31+
: options.defaultLayouts[`default`]
3932

4033
// No default layout set? Nothing to do here!
4134
if (!layoutPath) {
@@ -83,4 +76,4 @@ const gatsbyMdxLoader: LoaderDefinition = async function (source) {
8376
return out
8477
}
8578

86-
export default gatsbyMdxLoader
79+
export default gatsbyLayoutLoader
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* eslint-disable @babel/no-invalid-this */
2+
import type { LoaderDefinition } from "webpack"
3+
import type { ProcessorOptions } from "@mdx-js/mdx"
4+
import type { NodeMap } from "./types"
5+
6+
import { getOptions } from "loader-utils"
7+
8+
import compileMDX from "./compile-mdx"
9+
10+
export interface IGatsbyMDXLoaderOptions {
11+
options: ProcessorOptions
12+
nodeMap: NodeMap
13+
}
14+
15+
// Custom MDX Loader that injects the GraphQL MDX node into plugin data
16+
// This whole loaded could be replaced by @mdx-js/loader if MDX would
17+
// accept custom data passed to the unified pipeline via processor.data()
18+
const gatsbyMDXLoader: LoaderDefinition = async function (source) {
19+
const { options, nodeMap }: IGatsbyMDXLoaderOptions = getOptions(this)
20+
const res = nodeMap.get(this.resourcePath)
21+
22+
if (!res) {
23+
return source
24+
}
25+
26+
const { mdxNode, fileNode } = res
27+
28+
return compileMDX(source, mdxNode, fileNode, options)
29+
}
30+
31+
export default gatsbyMDXLoader

0 commit comments

Comments
 (0)