Skip to content

Commit d089bab

Browse files
authored
feat: allow accessing named exports (#114)
Co-authored-by: Adam Laycock <[email protected]> Co-authored-by: Kent C. Dodds <[email protected]> BREAKING CHANGE: This updates xdm to the latest version (which is [a major version bump](https://github.com/wooorm/xdm/releases/tag/3.0.0)). No breaking changes are expected, but we're being cautious.
1 parent 10167da commit d089bab

File tree

6 files changed

+108
-14
lines changed

6 files changed

+108
-14
lines changed

README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ the esbuild version mdx-bundler uses.
189189
- [Options](#options)
190190
- [Component Substitution](#component-substitution)
191191
- [Frontmatter and const](#frontmatter-and-const)
192+
- [Accessing named exports](#accessing-named-exports)
193+
- [Image Bundling](#image-bundling)
194+
- [bundleMDXFile](#bundlemdxfile)
192195
- [Known Issues](#known-issues)
193196
- [Inspiration](#inspiration)
194197
- [Other Solutions](#other-solutions)
@@ -527,6 +530,36 @@ export const exampleImage = 'https://example.com/image.jpg'
527530
<img src={exampleImage} alt="Image alt text" />
528531
```
529532

533+
### Accessing named exports
534+
535+
You can use `getMDXExport` instead of `getMDXComponent` to treat the mdx file as a module instead of just a component.
536+
It takes the same arguments that `getMDXComponent` does.
537+
538+
```mdx
539+
---
540+
title: Example Post
541+
---
542+
543+
export const toc = [
544+
{ depth: 1, value: 'The title' }
545+
]
546+
547+
# The title
548+
```
549+
550+
```js
551+
import * as React from 'react'
552+
import {getMDXExport} from 'mdx-bundler/client'
553+
554+
function MDXPage({code}: {code: string}) {
555+
const mdxExport = getMDXExport(code)
556+
console.log(mdxExport.toc) // [ { depth: 1, value: 'The title' } ]
557+
558+
const Component = React.useMemo(() => mdxExport.default, [code])
559+
560+
return <Component />
561+
}
562+
```
530563
### Image Bundling
531564

532565
With the [cwd](#cwd) and the remark plugin

package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,33 @@
4444
"@esbuild-plugins/node-resolve": "^0.1.4",
4545
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
4646
"gray-matter": "^4.0.3",
47-
"remark-frontmatter": "^4.0.0",
47+
"remark-frontmatter": "^4.0.1",
4848
"remark-mdx-frontmatter": "^1.0.1",
4949
"uuid": "^8.3.2",
50-
"xdm": "^2.1.0"
50+
"xdm": "^3.2.0"
5151
},
5252
"peerDependencies": {
5353
"esbuild": "0.11.x || 0.12.x || 0.13.x"
5454
},
5555
"devDependencies": {
56-
"@testing-library/react": "^12.0.0",
56+
"@testing-library/react": "^12.1.2",
5757
"@types/jsdom": "^16.2.13",
5858
"@types/mdx": "^2.0.1",
59-
"@types/react": "^17.0.17",
60-
"@types/react-dom": "^17.0.9",
59+
"@types/react": "^17.0.34",
60+
"@types/react-dom": "^17.0.11",
6161
"@types/uuid": "^8.3.1",
62-
"c8": "^7.8.0",
62+
"c8": "^7.10.0",
6363
"cross-env": "^7.0.3",
64-
"esbuild": "^0.12.20",
65-
"jsdom": "^17.0.0",
66-
"kcd-scripts": "^11.2.0",
64+
"esbuild": "^0.13.12",
65+
"jsdom": "^18.0.1",
66+
"kcd-scripts": "^11.2.2",
6767
"left-pad": "^1.3.0",
6868
"mdx-test-data": "^1.0.1",
6969
"react": "^17.0.2",
7070
"react-dom": "^17.0.2",
7171
"remark-mdx-images": "^1.0.3",
7272
"typescript": "^4.4.3",
73-
"uvu": "^0.5.1"
73+
"uvu": "^0.5.2"
7474
},
7575
"eslintConfig": {
7676
"extends": "./node_modules/kcd-scripts/eslint.js",

src/__tests__/index.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import rtl from '@testing-library/react'
88
import leftPad from 'left-pad'
99
import {remarkMdxImages} from 'remark-mdx-images'
1010
import {bundleMDX, bundleMDXFile} from '../index.js'
11-
import {getMDXComponent} from '../client.js'
11+
import {getMDXComponent, getMDXExport} from '../client.js'
1212

1313
const {render} = rtl
1414

@@ -394,6 +394,38 @@ test('should output assets', async () => {
394394
)
395395
})
396396

397+
test('should support importing named exports', async () => {
398+
const mdxSource = `
399+
---
400+
title: Example Post
401+
published: 2021-02-13
402+
description: This is some meta-data
403+
---
404+
405+
export const uncle = 'Bob'
406+
407+
# {uncle} was indeed the uncle
408+
`.trim()
409+
410+
const result = await bundleMDX(mdxSource)
411+
412+
/** @type {import('../types').MDXExport<{uncle: string}, {title: string, published: Date, description: string}>} */
413+
const mdxExport = getMDXExport(result.code)
414+
415+
// remark-mdx-frontmatter exports frontmatter
416+
assert.equal(mdxExport.frontmatter, {
417+
title: 'Example Post',
418+
published: new Date('2021-02-13'),
419+
description: 'This is some meta-data',
420+
})
421+
422+
assert.equal(mdxExport.uncle, 'Bob')
423+
424+
const {container} = render(React.createElement(mdxExport.default))
425+
426+
assert.equal(container.innerHTML, `<h1>Bob was indeed the uncle</h1>`)
427+
})
428+
397429
test('should support mdx from node_modules', async () => {
398430
const mdxSource = `
399431
import MdxData from 'mdx-test-data'

src/client.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,23 @@ import * as ReactDOM from 'react-dom'
1717
* @return {React.FunctionComponent<MDXContentProps>}
1818
*/
1919
function getMDXComponent(code, globals) {
20+
const mdxExport = getMDXExport(code, globals)
21+
return mdxExport.default
22+
}
23+
24+
/**
25+
* @template ExportedObject
26+
* @template Frontmatter
27+
* @type {import('./types').MDXExportFunction<ExportedObject, Frontmatter>}
28+
* @param {string} code - The string of code you got from bundleMDX
29+
* @param {Record<string, unknown>} [globals] - Any variables your MDX needs to have accessible when it runs
30+
*
31+
*/
32+
function getMDXExport(code, globals) {
2033
const scope = {React, ReactDOM, _jsx_runtime, ...globals}
2134
// eslint-disable-next-line
2235
const fn = new Function(...Object.keys(scope), code)
2336
return fn(...Object.values(scope))
2437
}
2538

26-
export {getMDXComponent}
39+
export {getMDXComponent, getMDXExport}

src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ async function bundleMDX(
195195
const code = decoder.write(Buffer.from(bundled.outputFiles[0].contents))
196196

197197
return {
198-
code: `${code};return Component.default;`,
198+
code: `${code};return Component;`,
199199
frontmatter: matter.data,
200200
errors: bundled.errors,
201201
matter,
@@ -214,7 +214,7 @@ async function bundleMDX(
214214
await unlink(path.join(buildOptions.outdir, fileName))
215215

216216
return {
217-
code: `${code};return Component.default;`,
217+
code: `${code};return Component`,
218218
frontmatter: matter.data,
219219
errors: bundled.errors,
220220
matter,

src/types.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,19 @@ type BundleMDXOptions = {
136136
options: GrayMatterOption<I, any>,
137137
) => GrayMatterOption<I, any>
138138
}
139+
140+
type MDXExport<
141+
ExportObject extends {},
142+
Frontmatter = {[key: string]: unknown},
143+
> = {
144+
default: React.FunctionComponent<MDXContentProps>
145+
frontmatter: Frontmatter
146+
} & ExportObject
147+
148+
type MDXExportFunction<
149+
ExportedObject extends {},
150+
Frontmatter extends Record<string, unknown>,
151+
> = (
152+
code: string,
153+
globals?: Record<string, unknown>,
154+
) => MDXExport<ExportedObject, Frontmatter>

0 commit comments

Comments
 (0)