Packages / @mdx-js/mdx
@mdx-js/mdx
MDX compiler.
This package is a compiler that turns MDX into JavaScript. It can also evaluate MDX code.
This is the core compiler for turning MDX into JavaScript which gives you the most control. If you’re using a bundler (Rollup, esbuild, webpack), a site builder (Next.js), or build system (Vite) which comes with a bundler, you’re better off using an integration: see § Integrations.
This package is ESM only. In Node.js (version 16+), install with npm:
npm install @mdx-js/mdx
In Deno with esm.sh
:
import {compile} from 'https://esm.sh/@mdx-js/mdx@2'
In browsers with esm.sh
:
<script type="module">
import {compile} from 'https://esm.sh/@mdx-js/mdx@2?bundle'
</script>
Say we have an MDX document, example.mdx
:
export function Thing() {
return <>World!</>
}
# Hello, <Thing />
…and some code in example.js
to compile example.mdx
to JavaScript:
import fs from 'node:fs/promises'
import {compile} from '@mdx-js/mdx'
const compiled = await compile(await fs.readFile('example.mdx'))
console.log(String(compiled))
Yields roughly:
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
export function Thing() {
return _jsx(_Fragment, {children: 'World!'})
}
function _createMdxContent(props) {
const _components = {h1: 'h1', ...props.components}
return _jsxs(_components.h1, {children: ['Hello, ', _jsx(Thing, {})]})
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || {}
return MDXLayout
? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})})
: _createMdxContent(props)
}
See § Using MDX for more on how MDX work and how to use the result.
This package exports the following identifiers: compile
, compileSync
, createProcessor
, evaluate
, evaluateSync
, nodeTypes
, run
, and runSync
. There is no default export.
compile(file, options?)
Compile MDX to JS.
file
(Compatible
from vfile
) — MDX document to parseoptions
(CompileOptions
, optional) — compile configurationPromise to compiled file (Promise<VFile>
).
The input value for file
can be many different things. You can pass a string
, Uint8Array
in UTF-8, VFile
, or anything that can be given to new VFile
.
import {compile} from '@mdx-js/mdx'
import {VFile} from 'vfile'
await compile(':)')
await compile(Buffer.from(':-)'))
await compile({path: 'path/to/file.mdx', value: '🥳'})
await compile(new VFile({path: 'path/to/file.mdx', value: '🤭'}))
The output VFile
can be used to access more than the generated code:
import {compile} from '@mdx-js/mdx'
import remarkPresetLintConsistent from 'remark-preset-lint-consistent' // Lint rules to check for consistent markdown.
import {reporter} from 'vfile-reporter'
const file = await compile('*like this* or _like this_?', {remarkPlugins: [remarkPresetLintConsistent]})
console.error(reporter(file))
Yields:
1:16-1:27 warning Emphasis should use `*` as a marker emphasis-marker remark-lint
⚠ 1 warning
compileSync(file, options?)
Synchronously compile MDX to JS.
When possible please use the async compile
.
file
(Compatible
from vfile
) — MDX document to parseoptions
(CompileOptions
, optional) — compile configurationCompiled file (VFile
).
createProcessor(options?)
Create a processor to compile markdown or MDX to JavaScript.
Note:
format: 'detect'
is not allowed inProcessorOptions
.
options
(ProcessorOptions
, optional) — process configurationProcessor (Processor
from unified
).
evaluate(file, options)
When you trust your content, evaluate
can work. When possible, use compile
, write to a file, and then run with Node or use one of the § Integrations.
☢️ Danger: it’s called evaluate because it
eval
s JavaScript.
file
(Compatible
from vfile
) — MDX document to parseoptions
(EvaluateOptions
, required) — configurationPromise to a module (Promise<MDXModule>
from mdx/types.js
).
The result is an object with a default
field set to the component; anything else that was exported is available too. For example, assuming the contents of example.mdx
from § Use was in file
, then:
import {evaluate} from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'
console.log(await evaluate(file, runtime))
…yields:
{Thing: [Function: Thing], default: [Function: MDXContent]}
Compiling (and running) MDX takes time.
If you are live-rendering a string of MDX that often changes using a virtual DOM based framework (such as React), one performance improvement is to call the MDXContent
component yourself. The reason is that the evaluate
creates a new function each time, which cannot be diffed:
const {default: MDXContent} = await evaluate('…')
-<MDXContent {...props} />
+MDXContent(props)
evaluateSync(file, options)
Compile and run MDX, synchronously.
When possible please use the async evaluate
.
☢️ Danger: it’s called evaluate because it
eval
s JavaScript.
file
(Compatible
from vfile
) — MDX document to parseoptions
(EvaluateOptions
, required) — configurationModule (MDXModule
from mdx/types.js
).
nodeTypes
List of node types made by mdast-util-mdx
, which have to be passed through untouched from the mdast tree to the hast tree (Array<string>
).
run(code, options)
Run code compiled with outputFormat: 'function-body'
.
☢️ Danger: this
eval
s JavaScript.
code
(VFile
or string
) — JavaScript function body to runoptions
(RunOptions
, required) — configurationPromise to a module (Promise<MDXModule>
from mdx/types.js
); the result is an object with a default
field set to the component; anything else that was exported is available too.
On the server:
import {compile} from '@mdx-js/mdx'
const code = String(await compile('# hi', {outputFormat: 'function-body'}))
// To do: send `code` to the client somehow.
On the client:
import {run} from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'
const code = '' // To do: get `code` from server somehow.
const {default: Content} = await run(code, {...runtime, baseUrl: import.meta.url})
console.log(Content)
…yields:
[Function: MDXContent]
runSync(code, options)
Run code, synchronously.
When possible please use the async run
.
☢️ Danger: this
eval
s JavaScript.
code
(VFile
or string
) — JavaScript function body to runoptions
(RunOptions
, required) — configurationModule (MDXModule
from mdx/types.js
).
CompileOptions
Configuration for compile
(TypeScript type).
CompileOptions
is the same as ProcessorOptions
with the exception that the format
option supports a 'detect'
value, which is the default. The 'detect'
format means to use 'md'
for files with an extension in mdExtensions
and 'mdx'
otherwise.
/**
* Configuration for `compile`
*/
type CompileOptions = Omit<ProcessorOptions, 'format'> & {
/**
* Format of `file` (default: `'detect'`).
*/
format?: 'detect' | 'md' | 'mdx' | null | undefined
}
EvaluateOptions
Configuration for evaluate
(TypeScript type).
EvaluateOptions
is the same as CompileOptions
, except that the options baseUrl
, jsx
, jsxImportSource
, jsxRuntime
, outputFormat
, pragma
, pragmaFrag
, pragmaImportSource
, and providerImportSource
are not allowed, and that RunOptions
are also used.
/**
* Configuration for `evaluate`.
*/
type EvaluateOptions = Omit<
CompileOptions,
| 'baseUrl' // Note that this is also in `RunOptions`.
| 'jsx'
| 'jsxImportSource'
| 'jsxRuntime'
| 'outputFormat'
| 'pragma'
| 'pragmaFrag'
| 'pragmaImportSource'
| 'providerImportSource'
> &
RunOptions
Fragment
Represent the children, typically a symbol (TypeScript type).
type Fragment = unknown
Jsx
Create a production element (TypeScript type).
type
(unknown
) — element type: Fragment
symbol, tag name (string
), componentprops
(Props
) — element props and children
key
(string
or undefined
) — key to useElement from your framework (JSX.Element
).
JsxDev
Create a development element (TypeScript type).
type
(unknown
) — element type: Fragment
symbol, tag name (string
), componentprops
(Props
) — element props and children
key
(string
or undefined
) — key to useisStaticChildren
(boolean
) — whether two or more children are passed (in an array), which is whether jsxs
or jsx
would be usedsource
(Source
) — info about sourceself
(unknown
) — context object (this
)ProcessorOptions
Configuration for createProcessor
(TypeScript type).
Assuming …yields:SourceMapGenerator
(SourceMapGenerator
from source-map
, optional) — add a source map (object form) as the map
field on the resulting fileExpand example
example.mdx
from § Use exists, then:import fs from 'node:fs/promises'
import {compile} from '@mdx-js/mdx'
import {SourceMapGenerator} from 'source-map'
const file = await compile(
{path: 'example.mdx', value: await fs.readFile('example.mdx')},
{SourceMapGenerator}
)
console.log(file.map)
{
file: 'example.mdx',
mappings: ';;aAAaA,QAAQ;YAAQ;;;;;;;;iBAE3B',
names: ['Thing'],
sources: ['example.mdx'],
version: 3
}
Say we have a module …now running baseUrl
(URL
or string
, optional, example: import.meta.url
) — use this URL as import.meta.url
and resolve import
and export … from
relative to itExpand example
example.js
:import {compile} from '@mdx-js/mdx'
const code = 'export {number} from "./data.js"\n\n# hi'
const baseUrl = 'https://a.full/url' // Typically `import.meta.url`
console.log(String(await compile(code, {baseUrl})))
node example.js
yields:import {jsx as _jsx} from 'react/jsx-runtime'
export {number} from 'https://a.full/data.js'
function _createMdxContent(props) { /* … */ }
export default function MDXContent(props = {}) { /* … */ }
Say we had some MDX that references a component that can be passed or provided at runtime: And a module to evaluate that: …running that would normally (production) yield: …but if we add …and we’d run it again, we’d get:development
(boolean
, default: false
) — whether to add extra info to error messages in generated code and use the development automatic JSX runtime (Fragment
and jsxDEV
from /jsx-dev-runtime
); when using the webpack loader (@mdx-js/loader
) or the Rollup integration (@mdx-js/rollup
) through Vite, this is automatically inferred from how you configure those toolsExpand example
**Note**<NoteIcon />: some stuff.
import fs from 'node:fs/promises'
import {evaluate} from '@mdx-js/mdx'
import * as runtime from 'react/jsx-runtime'
const path = 'example.mdx'
const value = await fs.readFile(path)
const MDXContent = (await evaluate({path, value}, {...runtime, baseUrl: import.meta.url})).default
console.log(MDXContent({}))
Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it.
at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:27:9)
at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:15:20)
at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:9:9)
at main (…/example.js:11:15)
development: true
to our example:@@ -7,6 +7,6 @@
import fs from 'node:fs/promises'
-import * as runtime from 'react/jsx-runtime'
+import * as runtime from 'react/jsx-dev-runtime'
import {evaluate} from '@mdx-js/mdx'
const path = 'example.mdx'
const value = await fs.readFile(path)
-const MDXContent = (await evaluate({path, value}, {...runtime, baseUrl: import.meta.url})).default
+const MDXContent = (await evaluate({path, value}, {development: true, ...runtime, baseUrl: import.meta.url})).default
console.log(MDXContent({}))
Error: Expected component `NoteIcon` to be defined: you likely forgot to import, pass, or provide it.
It’s referenced in your code at `1:9-1:21` in `example.mdx`
provide it.
at _missingMdxReference (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:27:9)
at _createMdxContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:15:20)
at MDXContent (eval at run (…/@mdx-js/mdx/lib/run.js:18:10), <anonymous>:9:9)
at main (…/example.js:11:15)
elementAttributeNameCase
('html'
or 'react
, default: 'react'
) — casing to use for attribute names; HTML casing is for example class
, stroke-linecap
, xml:lang
; React casing is for example className
, strokeLinecap
, xmlLang
; for JSX components written in MDX, the author has to be aware of which framework they use and write code accordingly; for AST nodes generated by this project, this option configures it
format
('md'
or 'mdx'
, default: 'mdx'
) — format of the file; 'md'
means treat as markdown and 'mdx'
means treat as MDXExpand example
compile('…') // Seen as MDX.
compile('…', {format: 'mdx'}) // Seen as MDX.
compile('…', {format: 'md'}) // Seen as markdown.
If …yields this difference:jsx
(boolean
, default: false
) — whether to keep JSX; the default is to compile JSX away so that the resulting file is immediately runnable.Expand example
file
is the contents of example.mdx
from § Use, then:compile(file, {jsx: true})
-import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+/* @jsxRuntime automatic @jsxImportSource react */
export function Thing() {
- return _jsx(_Fragment, {children: 'World'})
+ return <>World!</>
}
function _createMdxContent(props) {
const _components = {
h1: 'h1',
...props.components
}
- return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]})
+ return <_components.h1>{"Hello "}<Thing /></_components.h1>
}
export default function MDXContent(props = {}) {
const {wrapper: MDXLayout} = props.components || {}
return MDXLayout
- ? _jsx(MDXLayout, {
- ...props,
- children: _jsx(_createMdxContent, props)
- })
+ ? <MDXLayout {...props}><_createMdxContent {...props} /></MDXLayout>
: _createMdxContent(props)
}
}
If …yields this difference:jsxImportSource
(string
, default: 'react'
) — place to import automatic JSX runtimes from; when in the automatic
runtime, this is used to define an import for Fragment
, jsx
, jsxDEV
, and jsxs
Expand example
file
is the contents of example.mdx
from § Use, then:compile(file, {jsxImportSource: 'preact'})
-import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from 'preact/jsx-runtime'
jsxRuntime
('automatic'
or 'classic'
, default: 'automatic'
) — JSX runtime to use; the automatic runtime compiles to import _jsx from '$importSource/jsx-runtime'\n_jsx('p')
; the classic runtime compiles to calls such as h('p')
👉 Note: support for the classic runtime is deprecated and will likely be removed in the next major version.
If file
is the contents of example.mdx
from § Use, then:
compile(file, {jsxRuntime: 'classic'})
…yields this difference:
-import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+import React from 'react'
export function Thing() {
- return _jsx(_Fragment, {children: 'World'})
+ return React.createElement(React.Fragment, null, 'World!')
}
…
With a module …yields: The The outputFormat
('function-body'
or 'program'
, default: 'program'
) — output format to generate; in most cases 'program'
should be used, it results in a whole program; internally evaluate
uses 'function-body'
to compile to code that can be passed to run
; in some cases, you might want what evaluate
does in separate steps, such as when compiling on the server and running on the client.Expand example
example.js
:import {compile} from '@mdx-js/mdx'
const code = 'export const no = 3.14\n\n# hi {no}'
console.log(String(await compile(code, {outputFormat: 'program'}))) // Default.
console.log(String(await compile(code, {outputFormat: 'function-body'})))
import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
export const no = 3.14
function _createMdxContent(props) { /* … */ }
export default function MDXContent(props = {}) { /* … */ }
'use strict'
const {Fragment: _Fragment, jsx: _jsx} = arguments[0]
const no = 3.14
function _createMdxContent(props) { /* … */ }
function MDXContent(props = {}) { /* … */ }
return {no, default: MDXContent}
'program'
format will use import statements to import the runtime (and optionally provider) and use an export statement to yield the MDXContent
component.'function-body'
format will get the runtime (and optionally provider) from arguments[0]
, rewrite export statements, and use a return statement to yield what was exported.
mdExtensions
(Array<string>
, default: ['.md', '.markdown', '.mdown', '.mkdn', '.mkd', '.mdwn', '.mkdown', '.ron']
) — list of markdown extensions, with dot affects § Integrations
mdxExtensions
(Array<string>
, default: ['.mdx']
) — list of MDX extensions, with dot; affects § Integrations
pragma
(string
, default: 'React.createElement'
) — pragma for JSX, used in the classic runtime as an identifier for function calls: <x />
to React.createElement('x')
; when changing this, you should also define pragmaFrag
and pragmaImportSource
too
👉 Note: support for the classic runtime is deprecated and will likely be removed in the next major version.
If file
is the contents of example.mdx
from § Use, then:
compile(file, {
jsxRuntime: 'classic',
pragma: 'preact.createElement',
pragmaFrag: 'preact.Fragment',
pragmaImportSource: 'preact/compat'
})
…yields this difference:
-import React from 'react'
+import preact from 'preact/compat'
export function Thing() {
- return React.createElement(React.Fragment, null, 'World!')
+ return preact.createElement(preact.Fragment, null, 'World!')
}
…
pragmaFrag
(string
, default: 'React.Fragment'
) — pragma for fragment symbol, used in the classic runtime as an identifier for unnamed calls: <>
to React.createElement(React.Fragment)
; when changing this, you should also define pragma
and pragmaImportSource
too
👉 Note: support for the classic runtime is deprecated and will likely be removed in the next major version.
pragmaImportSource
(string
, default: 'react'
) — where to import the identifier of pragma
from, used in the classic runtime; to illustrate, when pragma
is 'a.b'
and pragmaImportSource
is 'c'
the following will be generated: import a from 'c'
and things such as a.b('h1', {})
; when changing this, you should also define pragma
and pragmaFrag
too
👉 Note: support for the classic runtime is deprecated and will likely be removed in the next major version.
If …yields this difference:providerImportSource
(string
, optional, example: '@mdx-js/react'
) — place to import a provider from; normally it’s used for runtimes that support context (React, Preact), but it can be used to inject components into the compiled code; the module must export and identifier useMDXComponents
which is called without arguments to get an object of components (see UseMdxComponents
)Expand example
file
is the contents of example.mdx
from § Use, then:compile(file, {providerImportSource: '@mdx-js/react'})
import {Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+import {useMDXComponents as _provideComponents} from '@mdx-js/react'
export function Thing() {
return _jsx(_Fragment, {children: 'World'})
}
function _createMdxContent(props) {
const _components = {
h1: 'h1',
+ ..._provideComponents(),
...props.components
}
return _jsxs(_components.h1, {children: ['Hello ', _jsx(Thing, {})]})
}
export default function MDXContent(props = {}) {
- const {wrapper: MDXLayout} = props.components || {}
+ const {wrapper: MDXLayout} = {
+ ..._provideComponents(),
+ ...props.components
+ }
return MDXLayout
? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {})})
: _createMdxContent()
recmaPlugins
(PluggableList
from unified
, optional) — list of recma plugins; this is a new ecosystem, currently in beta, to transform esast trees (JavaScript)Expand example
import recmaMdxIsMdxComponent from 'recma-mdx-is-mdx-component'
await compile(file, {recmaPlugins: [recmaMdxIsMdxComponent]})
rehypePlugins
(PluggableList
from unified
, optional) — list of rehype pluginsExpand example
import rehypeKatex from 'rehype-katex' // Render math with KaTeX.
import remarkMath from 'remark-math' // Support math like `$so$`.
await compile(file, {rehypePlugins: [rehypeKatex], remarkPlugins: [remarkMath]})
await compile(file, {
// A plugin with options:
rehypePlugins: [[rehypeKatex, {strict: true, throwOnError: true}]],
remarkPlugins: [remarkMath]
})
remarkPlugins
(PluggableList
from unified
, optional) — list of remark pluginsExpand example
import remarkFrontmatter from 'remark-frontmatter' // YAML and such.
import remarkGfm from 'remark-gfm' // Tables, footnotes, strikethrough, task lists, literal URLs.
await compile(file, {remarkPlugins: [remarkGfm]}) // One plugin.
await compile(file, {remarkPlugins: [[remarkFrontmatter, 'toml']]}) // A plugin with options.
await compile(file, {remarkPlugins: [remarkGfm, remarkFrontmatter]}) // Two plugins.
await compile(file, {remarkPlugins: [[remarkGfm, {singleTilde: false}], remarkFrontmatter]}) // Two plugins, first w/ options.
remarkRehypeOptions
(Options
from remark-rehype
, optional) — options to pass through to remark-rehype
; the option allowDangerousHtml
will always be set to true
and the MDX nodes (see nodeTypes
) are passed through; In particular, you might want to pass configuration for footnotes if your content is not in EnglishExpand example
compile({value: '…'}, {remarkRehypeOptions: {clobberPrefix: 'comment-1'}})
stylePropertyNameCase
('css'
or 'dom
, default: 'dom'
) — casing to use for property names in style
objects; CSS casing is for example background-color
and -webkit-line-clamp
; DOM casing is for example backgroundColor
and WebkitLineClamp
; for JSX components written in MDX, the author has to be aware of which framework they use and write code accordingly; for AST nodes generated by this project, this option configures it
tableCellAlignToStyle
(boolean
, default: true
) — turn obsolete align
props on td
and th
into CSS style
props
RunOptions
Configuration to run compiled code (TypeScript type).
Fragment
, jsx
, and jsxs
are used when the code is compiled in production mode (development: false
). Fragment
and jsxDEV
are used when compiled in development mode (development: true
). useMDXComponents
is used when the code is compiled with providerImportSource: '#'
(the exact value of this compile option doesn’t matter).
Fragment
(Fragment
, required) — symbol to use for fragmentsbaseUrl
(URL
or string
, optional, example: import.meta.url
) — use this URL as import.meta.url
and resolve import
and export … from
relative to it; this option can also be given at compile time in CompileOptions
; you should pass this (likely at runtime), as you might get runtime errors when using import.meta.url
/ import
/ export … from
otherwisejsx
(Jsx
, optional) — function to generate an element with static children in production modejsxDEV
(JsxDev
, optional) — function to generate an element in development modejsxs
(Jsx
, optional) — function to generate an element with dynamic children in production modeuseMDXComponents
(UseMdxComponents
, optional) — function to get components to useA /jsx-runtime
module will expose Fragment
, jsx
, and jsxs
:
import * as runtime from 'react/jsx-runtime'
const {default: Content} = await evaluate('# hi', {...runtime, baseUrl: import.meta.url, ...otherOptions})
A /jsx-dev-runtime
module will expose Fragment
and jsxDEV
:
import * as runtime from 'react/jsx-dev-runtime'
const {default: Content} = await evaluate('# hi', {development: true, baseUrl: import.meta.url, ...runtime, ...otherOptions})
Our providers will expose useMDXComponents
:
import * as provider from '@mdx-js/react'
import * as runtime from 'react/jsx-runtime'
const {default: Content} = await evaluate('# hi', {...provider, ...runtime, baseUrl: import.meta.url, ...otherOptions})
UseMdxComponents
Get components (TypeScript type).
There are no parameters.
Components (MDXComponents
from mdx/types.js
).
This package is fully typed with TypeScript. It exports the additional types CompileOptions
, EvaluateOptions
, Fragment
, Jsx
, JsxDev
, ProcessorOptions
, RunOptions
, and UseMdxComponents
.
For types of evaluated MDX to work, make sure the TypeScript JSX
namespace is typed. This is done by installing and using the types of your framework, such as @types/react
. See § Types on our website for information.
To understand what this project does, it’s very important to first understand what unified does: please read through the unifiedjs/unified
readme (the part until you hit the API section is required reading).
@mdx-js/mdx
is a unified pipeline — wrapped so that most folks don’t need to know about unified: core.js#L65
. The processor goes through these steps:
The input is MDX (serialized markdown with embedded JSX, ESM, and expressions). The markdown is parsed with micromark/micromark
and the embedded JS with one of its extensions micromark/micromark-extension-mdxjs
(which in turn uses acorn). Then syntax-tree/mdast-util-from-markdown
and its extension syntax-tree/mdast-util-mdx
are used to turn the results from the parser into a syntax tree: mdast.
Markdown is closest to the source format. This is where remark plugins come in. Typically, there shouldn’t be much going on here. But perhaps you want to support GFM (tables and such) or frontmatter? Then you can add a plugin here: remark-gfm
or remark-frontmatter
, respectively.
After markdown, we go to hast (HTML). This transformation is done by syntax-tree/mdast-util-to-hast
. Wait, why, what is HTML needed? Part of the reason is that we care about HTML semantics: we want to know that something is an <a>
, not whether it’s a link with a resource ([text](url)
) or a reference to a defined link definition ([text][id]\n\n[id]: url
). So an HTML AST is closer to where we want to go. Another reason is that there are many things folks need when they go MDX -> JS, markdown -> HTML, or even folks who only process their HTML -> HTML: use cases other than MDX. By having a single AST in these cases and writing a plugin that works on that AST, that plugin can supports all these use cases (for example, rehypejs/rehype-highlight
for syntax highlighting or rehypejs/rehype-katex
for math). So, this is where rehype plugins come in: most of the plugins, probably.
Then we go to JavaScript: esast (JS; an AST which is compatible with estree but looks a bit more like other unist ASTs). This transformation is done by syntax-tree/hast-util-to-estree
. This is a new ecosystem that does not have utilities or plugins yet. But it’s where @mdx-js/mdx
does its thing: where it adds imports/exports, where it compiles JSX away into _jsx()
calls, and where it does the other cool things that it provides.
Finally, The output is serialized JavaScript. That final step is done by astring, a small and fast JS generator.
Projects maintained by the unified collective are compatible with maintained versions of Node.js.
When we cut a new major release, we drop support for unmaintained versions of Node. This means we try to keep the current release line, @mdx-js/mdx@^3
, compatible with Node.js 16.
See § Security on our website for information.
See § Contribute on our website for ways to get started. See § Support for ways to get help.
This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.