|
| 1 | +# mdast-util-mdx-jsx |
| 2 | + |
| 3 | +[![Build][build-badge]][build] |
| 4 | +[![Coverage][coverage-badge]][coverage] |
| 5 | +[![Downloads][downloads-badge]][downloads] |
| 6 | +[![Size][size-badge]][size] |
| 7 | +[![Sponsors][sponsors-badge]][collective] |
| 8 | +[![Backers][backers-badge]][collective] |
| 9 | +[![Chat][chat-badge]][chat] |
| 10 | + |
| 11 | +Extension for [`mdast-util-from-markdown`][from-markdown] and/or |
| 12 | +[`mdast-util-to-markdown`][to-markdown] to support MDX (or MDX.js) JSX. |
| 13 | +When parsing (`from-markdown`), must be combined with |
| 14 | +[`micromark-extension-mdx-jsx`][extension]. |
| 15 | + |
| 16 | +This utility handles parsing and serializing. |
| 17 | +See [`micromark-extension-mdx-jsx`][extension] for how the syntax works. |
| 18 | + |
| 19 | +You probably should use either `micromark-extension-mdx` with `mdast-util-mdx` |
| 20 | +or `micromark-extension-mdxjs` with `mdast-util-mdxjs` (which both include this |
| 21 | +package) to support all of MDX or MDX.js. |
| 22 | +Or use it all through `remark-mdx` or `remark-mdxjs` (**[remark][]**). |
| 23 | + |
| 24 | +## Install |
| 25 | + |
| 26 | +[npm][]: |
| 27 | + |
| 28 | +```sh |
| 29 | +npm install mdast-util-mdx-jsx |
| 30 | +``` |
| 31 | + |
| 32 | +## Use |
| 33 | + |
| 34 | +Say we have an MDX.js file, `example.mdx`: |
| 35 | + |
| 36 | +```mdx |
| 37 | +<Box> |
| 38 | + - a list |
| 39 | +</Box> |
| 40 | + |
| 41 | +<MyComponent {...props} /> |
| 42 | + |
| 43 | +<abbr title="Hypertext Markup Language">HTML</abbr> is a lovely language. |
| 44 | +``` |
| 45 | + |
| 46 | +And our script, `example.js`, looks as follows: |
| 47 | + |
| 48 | +```js |
| 49 | +var fs = require('fs') |
| 50 | +var acorn = require('acorn') |
| 51 | +var syntax = require('micromark-extension-mdx-jsx') |
| 52 | +var fromMarkdown = require('mdast-util-from-markdown') |
| 53 | +var toMarkdown = require('mdast-util-to-markdown') |
| 54 | +var mdxJsx = require('mdast-util-mdx-jsx') |
| 55 | + |
| 56 | +var doc = fs.readFileSync('example.mdx') |
| 57 | + |
| 58 | +var tree = fromMarkdown(doc, { |
| 59 | + extensions: [syntax({acorn: acorn, addResult: true})], |
| 60 | + mdastExtensions: [mdxJsx.fromMarkdown] |
| 61 | +}) |
| 62 | + |
| 63 | +console.log(tree) |
| 64 | + |
| 65 | +var out = toMarkdown(tree, {extensions: [mdxJsx.toMarkdown]}) |
| 66 | + |
| 67 | +console.log(out) |
| 68 | +``` |
| 69 | + |
| 70 | +Now, running `node example` yields (positional info removed for brevity): |
| 71 | + |
| 72 | +```js |
| 73 | +{ |
| 74 | + type: 'root', |
| 75 | + children: [ |
| 76 | + { |
| 77 | + type: 'mdxJsxFlowElement', |
| 78 | + name: 'Box', |
| 79 | + attributes: [], |
| 80 | + children: [ |
| 81 | + { |
| 82 | + type: 'list', |
| 83 | + ordered: false, |
| 84 | + start: null, |
| 85 | + spread: false, |
| 86 | + children: [ |
| 87 | + { |
| 88 | + type: 'listItem', |
| 89 | + spread: false, |
| 90 | + checked: null, |
| 91 | + children: [ |
| 92 | + { |
| 93 | + type: 'paragraph', |
| 94 | + children: [{type: 'text', value: 'a list'}] |
| 95 | + } |
| 96 | + ] |
| 97 | + } |
| 98 | + ] |
| 99 | + } |
| 100 | + ] |
| 101 | + }, |
| 102 | + { |
| 103 | + type: 'mdxJsxFlowElement', |
| 104 | + name: 'MyComponent', |
| 105 | + attributes: [ |
| 106 | + { |
| 107 | + type: 'mdxJsxExpressionAttribute', |
| 108 | + value: '...props', |
| 109 | + data: { |
| 110 | + estree: { |
| 111 | + type: 'SpreadElement', |
| 112 | + argument: {type: 'Identifier', name: 'props'} |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + ], |
| 117 | + children: [] |
| 118 | + }, |
| 119 | + { |
| 120 | + type: 'paragraph', |
| 121 | + children: [ |
| 122 | + { |
| 123 | + type: 'mdxJsxTextElement', |
| 124 | + name: 'abbr', |
| 125 | + attributes: [ |
| 126 | + { |
| 127 | + type: 'mdxJsxAttribute', |
| 128 | + name: 'title', |
| 129 | + value: 'Hypertext Markup Language' |
| 130 | + } |
| 131 | + ], |
| 132 | + children: [{type: 'text', value: 'HTML'}] |
| 133 | + }, |
| 134 | + {type: 'text', value: ' is a lovely language.'} |
| 135 | + ] |
| 136 | + } |
| 137 | + ] |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +```markdown |
| 142 | +<Box> |
| 143 | + * a list |
| 144 | +</Box> |
| 145 | + |
| 146 | +<MyComponent {...props}/> |
| 147 | + |
| 148 | +<abbr title="Hypertext Markup Language">HTML</abbr> is a lovely language. |
| 149 | +``` |
| 150 | + |
| 151 | +## API |
| 152 | + |
| 153 | +### `mdxJsx.fromMarkdown` |
| 154 | + |
| 155 | +### `mdxJsx.toMarkdown` |
| 156 | + |
| 157 | +> Note: the separate extensions are also available at |
| 158 | +> `mdast-util-mdx-jsx/from-markdown` and |
| 159 | +> `mdast-util-mdx-jsx/to-markdown`. |
| 160 | +
|
| 161 | +Support MDX (or MDX.js) JSX. |
| 162 | +The exports are extensions, respectively for |
| 163 | +[`mdast-util-from-markdown`][from-markdown] and |
| 164 | +[`mdast-util-to-markdown`][to-markdown]. |
| 165 | + |
| 166 | +When using the [syntax extension][extension] with `addResult`, nodes will have a |
| 167 | +`data.estree` field set to an [ESTree][]. |
| 168 | + |
| 169 | +There are no options, but passing [`options.quote`][quote] to |
| 170 | +`mdast-util-to-markdown` is honored for attributes. |
| 171 | + |
| 172 | +## Syntax tree |
| 173 | + |
| 174 | +The following interfaces are added to **[mdast][]** by this utility. |
| 175 | + |
| 176 | +### Nodes |
| 177 | + |
| 178 | +#### `MDXJsxFlowElement` |
| 179 | + |
| 180 | +```idl |
| 181 | +interface MDXJsxFlowElement <: Parent { |
| 182 | + type: "mdxJsxFlowElement" |
| 183 | +} |
| 184 | +
|
| 185 | +MDXJsxFlowElement includes MDXJsxElement |
| 186 | +``` |
| 187 | + |
| 188 | +**MDXJsxFlowElement** (**[Parent][dfn-parent]**) represents JSX in flow (block). |
| 189 | +It can be used where **[flow][dfn-content-flow]** content is expected. |
| 190 | +It includes the mixin **[MDXJsxElement][dfn-mixin-mdx-jsx-element]**. |
| 191 | + |
| 192 | +For example, the following markdown: |
| 193 | + |
| 194 | +```markdown |
| 195 | +<w x="y"> |
| 196 | + z |
| 197 | +</w> |
| 198 | +``` |
| 199 | + |
| 200 | +Yields: |
| 201 | + |
| 202 | +```js |
| 203 | +{ |
| 204 | + type: 'mdxJsxFlowElement', |
| 205 | + name: 'w', |
| 206 | + attributes: [{type: 'mdxJsxAttribute', name: 'x', value: 'y'}], |
| 207 | + children: [{type: 'paragraph', children: [{type: 'text', value: 'z'}]}] |
| 208 | +} |
| 209 | +``` |
| 210 | + |
| 211 | +#### `MDXJsxTextElement` |
| 212 | + |
| 213 | +```idl |
| 214 | +interface MDXJsxTextElement <: Parent { |
| 215 | + type: "mdxJsxTextElement" |
| 216 | +} |
| 217 | +
|
| 218 | +MDXJsxTextElement includes MDXJsxElement |
| 219 | +``` |
| 220 | + |
| 221 | +**MDXJsxTextElement** (**[Parent][dfn-parent]**) represents JSX in text (span, |
| 222 | +inline). |
| 223 | +It can be used where **[phrasing][dfn-content-phrasing]** content is |
| 224 | +expected. |
| 225 | +It includes the mixin **[MDXJsxElement][dfn-mixin-mdx-jsx-element]**. |
| 226 | + |
| 227 | +For example, the following markdown: |
| 228 | + |
| 229 | +```markdown |
| 230 | +a <b c>d</b> e. |
| 231 | +``` |
| 232 | + |
| 233 | +Yields: |
| 234 | + |
| 235 | +```js |
| 236 | +{ |
| 237 | + type: 'mdxJsxTextElement', |
| 238 | + name: 'b', |
| 239 | + attributes: [{type: 'mdxJsxAttribute', name: 'c', value: null}], |
| 240 | + children: [{type: 'text', value: 'd'}] |
| 241 | +} |
| 242 | +``` |
| 243 | + |
| 244 | +### Mixin |
| 245 | + |
| 246 | +### `MDXJsxElement` |
| 247 | + |
| 248 | +```idl |
| 249 | +interface mixin MDXJsxElement { |
| 250 | + name: string? |
| 251 | + attributes: [MDXJsxExpressionAttribute | MDXJsxAttribute] |
| 252 | +} |
| 253 | +
|
| 254 | +interface MDXJsxExpressionAttribute <: Literal { |
| 255 | + type: "mdxJsxExpressionAttribute" |
| 256 | +} |
| 257 | +
|
| 258 | +interface MDXJsxAttribute <: Node { |
| 259 | + type: "mdxJsxAttribute" |
| 260 | + name: string |
| 261 | + value: MDXJsxAttributeValueExpression | string? |
| 262 | +} |
| 263 | +
|
| 264 | +interface MDXJsxAttributeValueExpression <: Literal { |
| 265 | + type: "mdxJsxAttributeValueExpression" |
| 266 | +} |
| 267 | +``` |
| 268 | + |
| 269 | +**MDXJsxElement** represents a JSX element. |
| 270 | + |
| 271 | +The `name` field can be present and represents an identifier. |
| 272 | +Without `name`, the element represents a fragment, in which case no attributes |
| 273 | +must be present. |
| 274 | + |
| 275 | +The `attributes` field represents information associated with the node. |
| 276 | +The value of the `attributes` field is a list of **MDXJsxExpressionAttribute** |
| 277 | +and **MDXJsxAttribute** nodes. |
| 278 | + |
| 279 | +**MDXJsxExpressionAttribute** represents an expression (typically in a |
| 280 | +programming language) that when evaluated results in multiple attributes. |
| 281 | + |
| 282 | +**MDXJsxAttribute** represents a single attribute. |
| 283 | +The `name` field must be present. |
| 284 | +The `value` field can be present, in which case it is either a string (a static |
| 285 | +value) or an expression (typically in a programming language) that when |
| 286 | +evaluated results in an attribute value. |
| 287 | + |
| 288 | +### Content model |
| 289 | + |
| 290 | +#### `FlowContent` (MDX JSX) |
| 291 | + |
| 292 | +```idl |
| 293 | +type MDXJsxFlowContent = MDXJsxFlowElement | FlowContent |
| 294 | +``` |
| 295 | + |
| 296 | +#### `PhrasingContent` (MDX JSX) |
| 297 | + |
| 298 | +```idl |
| 299 | +type MDXJsxPhrasingContent = MDXJsxTextElement | PhrasingContent |
| 300 | +``` |
| 301 | + |
| 302 | +## Related |
| 303 | + |
| 304 | +* [`remarkjs/remark`][remark] |
| 305 | + — markdown processor powered by plugins |
| 306 | +* `remarkjs/remark-mdx` |
| 307 | + — remark plugin to support MDX |
| 308 | +* `remarkjs/remark-mdxjs` |
| 309 | + — remark plugin to support MDX.js |
| 310 | +* [`syntax-tree/mdast-util-from-markdown`][from-markdown] |
| 311 | + — mdast parser using `micromark` to create mdast from markdown |
| 312 | +* [`syntax-tree/mdast-util-to-markdown`][to-markdown] |
| 313 | + — mdast serializer to create markdown from mdast |
| 314 | +* `syntax-tree/mdast-util-mdx` |
| 315 | + — mdast utility to support all of MDX |
| 316 | +* `syntax-tree/mdast-util-mdxjs` |
| 317 | + — mdast utility to support all of MDX.js |
| 318 | +* [`micromark/micromark`][micromark] |
| 319 | + — the smallest commonmark-compliant markdown parser that exists |
| 320 | +* [`micromark/micromark-extension-mdx-jsx`][extension] |
| 321 | + — micromark extension to parse JSX |
| 322 | + |
| 323 | +## Contribute |
| 324 | + |
| 325 | +See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get |
| 326 | +started. |
| 327 | +See [`support.md`][support] for ways to get help. |
| 328 | + |
| 329 | +This project has a [code of conduct][coc]. |
| 330 | +By interacting with this repository, organization, or community you agree to |
| 331 | +abide by its terms. |
| 332 | + |
| 333 | +## License |
| 334 | + |
| 335 | +[MIT][license] © [Titus Wormer][author] |
| 336 | + |
| 337 | +[build-badge]: https://github.com/syntax-tree/mdast-util-mdx-jsx/workflows/main/badge.svg |
| 338 | + |
| 339 | +[build]: https://github.com/syntax-tree/mdast-util-mdx-jsx/actions |
| 340 | + |
| 341 | +[coverage-badge]: https://img.shields.io/codecov/c/github/syntax-tree/mdast-util-mdx-jsx.svg |
| 342 | + |
| 343 | +[coverage]: https://codecov.io/github/syntax-tree/mdast-util-mdx-jsx |
| 344 | + |
| 345 | +[downloads-badge]: https://img.shields.io/npm/dm/mdast-util-mdx-jsx.svg |
| 346 | + |
| 347 | +[downloads]: https://www.npmjs.com/package/mdast-util-mdx-jsx |
| 348 | + |
| 349 | +[size-badge]: https://img.shields.io/bundlephobia/minzip/mdast-util-mdx-jsx.svg |
| 350 | + |
| 351 | +[size]: https://bundlephobia.com/result?p=mdast-util-mdx-jsx |
| 352 | + |
| 353 | +[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg |
| 354 | + |
| 355 | +[backers-badge]: https://opencollective.com/unified/backers/badge.svg |
| 356 | + |
| 357 | +[collective]: https://opencollective.com/unified |
| 358 | + |
| 359 | +[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg |
| 360 | + |
| 361 | +[chat]: https://github.com/syntax-tree/unist/discussions |
| 362 | + |
| 363 | +[npm]: https://docs.npmjs.com/cli/install |
| 364 | + |
| 365 | +[license]: license |
| 366 | + |
| 367 | +[author]: https://wooorm.com |
| 368 | + |
| 369 | +[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md |
| 370 | + |
| 371 | +[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md |
| 372 | + |
| 373 | +[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md |
| 374 | + |
| 375 | +[mdast]: https://github.com/syntax-tree/mdast |
| 376 | + |
| 377 | +[remark]: https://github.com/remarkjs/remark |
| 378 | + |
| 379 | +[from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown |
| 380 | + |
| 381 | +[to-markdown]: https://github.com/syntax-tree/mdast-util-to-markdown |
| 382 | + |
| 383 | +[micromark]: https://github.com/micromark/micromark |
| 384 | + |
| 385 | +[extension]: https://github.com/micromark/micromark-extension-mdxjs-esm |
| 386 | + |
| 387 | +[quote]: https://github.com/syntax-tree/mdast-util-to-markdown#optionsquote |
| 388 | + |
| 389 | +[estree]: https://github.com/estree/estree |
| 390 | + |
| 391 | +[dfn-parent]: https://github.com/syntax-tree/mdast#parent |
| 392 | + |
| 393 | +[dfn-content-flow]: #flowcontent-mdx-jsx |
| 394 | + |
| 395 | +[dfn-content-phrasing]: #phrasingcontent-mdx-jsx |
| 396 | + |
| 397 | +[dfn-mixin-mdx-jsx-element]: #mdxjsxelement |
0 commit comments