Skip to content

Commit 99b07e3

Browse files
committed
properly inject compiled MDX as children
1 parent 0b0d484 commit 99b07e3

File tree

8 files changed

+159
-21
lines changed

8 files changed

+159
-21
lines changed

benchmarks/gabe-fs-mdx/src/templates/blog-post.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,13 @@ import { graphql, Link } from "gatsby"
33
import Layout from "../components/layout_1"
44

55
const Article = ({ children, pageContext, data }) => {
6-
console.log({ data })
76
return (
87
<Layout>
98
<Link to="/">Go back to index page</Link>
109
<div>
1110
<h1>{pageContext.frontmatter.title}</h1>
1211
{children}
1312
</div>
14-
<pre>
15-
<code>{JSON.stringify({ children, pageContext, data }, null, 2)}</code>
16-
</pre>
1713
</Layout>
1814
)
1915
}

packages/gatsby-plugin-mdx/package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@
6161
"rehype-infer-description-meta": "^1.0.1",
6262
"unified": "^10.1.2",
6363
"unist-util-visit": "^4.1.0",
64-
"vfile": "^5.3.2"
64+
"vfile": "^5.3.2",
65+
"acorn": "^8.7.1",
66+
"acorn-jsx": "^5.3.2",
67+
"astring": "^1.8.3",
68+
"estree-util-build-jsx": "^2.1.0"
6569
},
6670
"devDependencies": {
6771
"@babel/cli": "^7.17.10",
@@ -73,6 +77,7 @@
7377
},
7478
"peerDependencies": {
7579
"@mdx-js/react": "^2.0.0",
80+
"@types/estree": "^0.0.50",
7681
"gatsby": "^4.0.0-next",
7782
"gatsby-source-filesystem": "^4.0.0-next",
7883
"loader-utils": "^3.2.0",

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

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable @babel/no-invalid-this */
22
import type { LoaderDefinition } from "webpack"
3+
import type { Program, Identifier, ExportDefaultDeclaration } from "estree"
34
import type { NodeMap } from "./types"
45
import type { IMdxPluginOptions } from "./plugin-options"
56

@@ -24,8 +25,127 @@ const gatsbyLayoutLoader: LoaderDefinition = async function (source) {
2425
)
2526
}
2627

27-
// @todo this should be an AST transformation and not string manipulation
28-
return `import RENDERED_MDX from "${mdxPath}"\n${source}\nconsole.log(RENDERED_MDX)`
28+
const acorn = await import(`acorn`)
29+
const { default: jsx } = await import(`acorn-jsx`)
30+
const { generate } = await import(`astring`)
31+
const { buildJsx } = await import(`estree-util-build-jsx`)
32+
33+
try {
34+
const tree = acorn.Parser.extend(jsx()).parse(source, {
35+
ecmaVersion: 2020,
36+
sourceType: `module`,
37+
locations: true,
38+
})
39+
40+
const AST = tree as unknown as Program
41+
42+
// Throw when tree is not a Program
43+
if (!AST.body && !AST.sourceType) {
44+
throw new Error(
45+
`Invalid AST. Parsed source code did not return a Program`
46+
)
47+
}
48+
49+
// Inject import to actual MDX file at the top of the file
50+
AST.body.unshift({
51+
type: `ImportDeclaration`,
52+
specifiers: [
53+
{
54+
type: `ImportDefaultSpecifier`,
55+
local: {
56+
type: `Identifier`,
57+
name: `GATSBY_COMPILED_MDX`,
58+
},
59+
},
60+
],
61+
source: {
62+
type: `Literal`,
63+
value: mdxPath,
64+
},
65+
})
66+
67+
// Replace default export with wrapper function that injects compiled MDX as children
68+
AST.body = AST.body.map(child => {
69+
if (child.type !== `ExportDefaultDeclaration`) {
70+
return child
71+
}
72+
const declaration = child.declaration as unknown as Identifier
73+
if (!declaration.name) {
74+
throw new Error(`Unable to determine default export name`)
75+
}
76+
77+
const pageComponentName = declaration.name
78+
79+
return {
80+
type: `ExportDefaultDeclaration`,
81+
declaration: {
82+
type: `ArrowFunctionExpression`,
83+
id: null,
84+
expression: true,
85+
generator: false,
86+
async: false,
87+
params: [
88+
{
89+
type: `Identifier`,
90+
name: `props`,
91+
},
92+
],
93+
body: {
94+
type: `JSXElement`,
95+
openingElement: {
96+
type: `JSXOpeningElement`,
97+
attributes: [
98+
{
99+
type: `JSXSpreadAttribute`,
100+
argument: {
101+
type: `Identifier`,
102+
name: `props`,
103+
},
104+
},
105+
],
106+
name: {
107+
type: `JSXIdentifier`,
108+
name: pageComponentName,
109+
},
110+
selfClosing: false,
111+
},
112+
closingElement: {
113+
type: `JSXClosingElement`,
114+
name: {
115+
type: `JSXIdentifier`,
116+
name: pageComponentName,
117+
},
118+
},
119+
children: [
120+
{
121+
type: `JSXExpressionContainer`,
122+
expression: {
123+
type: `CallExpression`,
124+
callee: {
125+
type: `Identifier`,
126+
name: `GATSBY_COMPILED_MDX`,
127+
},
128+
arguments: [],
129+
optional: false,
130+
},
131+
},
132+
],
133+
},
134+
},
135+
} as unknown as ExportDefaultDeclaration
136+
})
137+
138+
// @todo what do we do with runtime, pragma and pragmaFrag options? We should still be able to support preact.
139+
buildJsx(AST)
140+
141+
const transformedSource = generate(AST)
142+
143+
return transformedSource
144+
} catch (e) {
145+
throw new Error(
146+
`Unable to inject MDX into JS template:\n${this.resourcePath}\n${e}`
147+
)
148+
}
29149
}
30150

31151
export default gatsbyLayoutLoader

packages/gatsby-plugin-mdx/src/gatsby-mdx-loader.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { LoaderDefinition } from "webpack"
33
import type { ProcessorOptions } from "@mdx-js/mdx"
44
import type { NodeMap } from "./types"
55

6+
import grayMatter from "gray-matter"
67
import { getOptions } from "loader-utils"
78

89
import { compileMDX } from "./compile-mdx"
@@ -25,9 +26,11 @@ const gatsbyMDXLoader: LoaderDefinition = async function (source) {
2526

2627
const { mdxNode, fileNode } = res
2728

29+
// Remove frontmatter
30+
const { content } = grayMatter(source)
31+
2832
const { processedMDX } = await compileMDX(
29-
// We want to work with the transformed source from our layout plugin
30-
{ ...mdxNode, body: source },
33+
{ ...mdxNode, body: content },
3134
fileNode,
3235
options
3336
)

packages/gatsby-plugin-mdx/src/get-source-plugins-as-remark-plugins.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ export async function getSourcePluginsAsRemarkPlugins({
2323
}: IGetSourcePluginsAsRemarkPlugins): Promise<
2424
ProcessorOptions["remarkPlugins"]
2525
> {
26-
const userPluginsFiltered = gatsbyRemarkPlugins.filter(
27-
plugin => typeof plugin.module === `function`
28-
)
26+
const userPluginsFiltered = gatsbyRemarkPlugins
27+
? gatsbyRemarkPlugins.filter(plugin => typeof plugin.module === `function`)
28+
: []
2929

3030
if (!userPluginsFiltered.length) {
3131
return []

packages/gatsby-plugin-mdx/src/plugin-options.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ import rehypeMdxMetadataExtractor from "./rehype-metadata-extractor"
1212
import { remarkMdxHtmlPlugin } from "./remark-mdx-html-plugin"
1313
import { remarkPathPlugin } from "./remark-path-prefix-plugin"
1414

15-
export interface IMdxPluginOptions extends PluginOptions {
15+
export interface IMdxPluginOptions extends Partial<PluginOptions> {
1616
extensions: [string]
17-
defaultLayouts: { [key: string]: string }
1817
mdxOptions: ProcessorOptions
19-
gatsbyRemarkPlugins: [IPluginRefObject]
18+
gatsbyRemarkPlugins?: [IPluginRefObject]
2019
}
2120
interface IHelpers {
2221
getNode: NodePluginArgs["getNode"]
@@ -37,7 +36,6 @@ export const defaultOptions: MdxDefaultOptions = pluginOptions => {
3736
const options: IMdxPluginOptions = deepmerge(
3837
{
3938
extensions: [`.mdx`],
40-
defaultLayouts: {},
4139
mdxOptions,
4240
},
4341
pluginOptions
@@ -70,7 +68,14 @@ export const enhanceMdxOptions: EnhanceMdxOptions = async (
7068
}
7169

7270
// Support gatsby-remark-* plugins
73-
if (Object.keys(options.gatsbyRemarkPlugins).length) {
71+
if (
72+
options.gatsbyRemarkPlugins &&
73+
Object.keys(options.gatsbyRemarkPlugins).length
74+
) {
75+
if (!options.mdxOptions.remarkPlugins) {
76+
options.mdxOptions.remarkPlugins = []
77+
}
78+
7479
// Parser plugins
7580
for (const plugin of options.gatsbyRemarkPlugins) {
7681
const requiredPlugin = plugin.module

packages/gatsby/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ export interface GatsbySSR<
791791
}
792792

793793
export interface PluginOptions {
794-
plugins: unknown[]
794+
// plugins: unknown[]
795795
[key: string]: unknown
796796
}
797797

yarn.lock

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5825,7 +5825,7 @@ acorn-import-assertions@^1.7.6:
58255825
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.7.6.tgz#580e3ffcae6770eebeec76c3b9723201e9d01f78"
58265826
integrity sha512-FlVvVFA1TX6l3lp8VjDnYYq7R1nyW6x3svAt4nDgrWQ9SBaSh9CnbwgSUTasgfNfOG5HlM1ehugCvM+hjo56LA==
58275827

5828-
acorn-jsx@^5.0.0:
5828+
acorn-jsx@^5.0.0, acorn-jsx@^5.3.2:
58295829
version "5.3.2"
58305830
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
58315831
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
@@ -5868,7 +5868,7 @@ acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1, acorn@^7.4.0:
58685868
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
58695869
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
58705870

5871-
acorn@^8.0.0:
5871+
acorn@^8.0.0, acorn@^8.7.1:
58725872
version "8.7.1"
58735873
resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
58745874
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
@@ -6426,7 +6426,7 @@ astral-regex@^2.0.0:
64266426
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
64276427
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
64286428

6429-
astring@^1.6.0:
6429+
astring@^1.6.0, astring@^1.8.3:
64306430
version "1.8.3"
64316431
resolved "https://registry.npmjs.org/astring/-/astring-1.8.3.tgz#1a0ae738c7cc558f8e5ddc8e3120636f5cebcb85"
64326432
integrity sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A==
@@ -10873,6 +10873,15 @@ estree-util-build-jsx@^2.0.0:
1087310873
estree-util-is-identifier-name "^2.0.0"
1087410874
estree-walker "^3.0.0"
1087510875

10876+
estree-util-build-jsx@^2.1.0:
10877+
version "2.1.0"
10878+
resolved "https://registry.yarnpkg.com/estree-util-build-jsx/-/estree-util-build-jsx-2.1.0.tgz#629aa81fbb1b16ed628c7a9334d37bc8a2a3726f"
10879+
integrity sha512-gsBGfsY6LOJUIDwmMkTOcgCX+3r/LUjRBccgHMSW55PHjhZsV13RmPl/iwpAvW8KcQqoN9P0FEFWTSS2Zc5bGA==
10880+
dependencies:
10881+
"@types/estree-jsx" "^0.0.1"
10882+
estree-util-is-identifier-name "^2.0.0"
10883+
estree-walker "^3.0.0"
10884+
1087610885
estree-util-is-identifier-name@^2.0.0:
1087710886
version "2.0.0"
1087810887
resolved "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.0.0.tgz#e2d3d2ae3032c017b2112832bfc5d8ba938c8010"

0 commit comments

Comments
 (0)