unified

Project: micromark/micromark-extension-mdx-jsx

Package: micromark-extension-mdx-jsx@1.0.3

  1. Dependents: 0
  2. micromark extension to support MDX or MDX.js JSX
  1. unified 178
  2. markdown 140
  3. micromark 36
  4. mdx 25
  5. javascript 24
  6. micromark-extension 16
  7. jsx 9
  8. ecmascript 9
  9. mdxjs 8
  10. js 7
  11. es 2

micromark-extension-mdx-jsx

Build Coverage Downloads Size Sponsors Backers Chat

micromark extension to support MDX JSX (<Component />).

Contents

What is this?

This package contains extensions that add support for JSX enabled by MDX to micromark. It mostly matches how JSX works in most places that support it (TypeScript, Babel, esbuild, etc).

When to use this

These tools are all low-level. In many cases, you want to use remark-mdx with remark instead. When you are using mdx-js/mdx, that is already included.

Even when you want to use micromark, you likely want to use micromark-extension-mdxjs to support all MDX features. That extension includes this extension.

When working with mdast-util-from-markdown, you must combine this package with mdast-util-mdx-jsx.

Install

This package is ESM only. In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with npm:

npm install micromark-extension-mdx-jsx

In Deno with esm.sh:

import {mdxJsx} from 'https://esm.sh/micromark-extension-mdx-jsx@1'

In browsers with esm.sh:

<script type="module">
  import {mdxJsx} from 'https://esm.sh/micromark-extension-mdx-jsx@1?bundle'
</script>

Use

import {micromark} from 'micromark'
import {mdxJsx} from 'micromark-extension-mdx-jsx'

const output = micromark('a <b c d="e" /> f', {extensions: [mdxJsx()]})

console.log(output)

Yields:

<p>a  f</p>

…which is useless: go to a syntax tree with mdast-util-from-markdown and mdast-util-mdx-jsx instead.

API

This package exports the identifier mdxJsx. There is no default export.

The export map supports the endorsed development condition. Run node --conditions development module.js to get instrumented dev code. Without this condition, production code is loaded.

mdxJsx(options?)

Add support for parsing JSX in markdown.

Function that can be called to get a syntax extension for micromark (passed in extensions).

options

Configuration (optional).

options.acorn

Acorn parser to use (Acorn, optional).

options.acornOptions

Options to pass to acorn (Object, default: {ecmaVersion: 2020, sourceType: 'module'}). All fields can be set. Positional info (loc, range) is set on ES nodes regardless of acorn options.

options.addResult

Whether to add an estree field to the mdxTextJsx and mdxFlowJsx tokens with the results from acorn (boolean, default: false).

Authoring

When authoring markdown with JSX, keep in mind that MDX is a whitespace sensitive and line-based language, while JavaScript is insensitive to whitespace. This affects how markdown and JSX interleave with eachother in MDX. For more info on how it works, see § Interleaving on the MDX site.

Some features of JS(X) are not supported, notably:

Comments inside tags

JavaScript comments in JSX are not supported.

Incorrect:

<hi/*comment!*//>
<hello// comment!
/>

Correct:

<hi/>
<hello
/>

A PR that adds support for them would be accepted.

Element or fragment attribute values

JSX elements or JSX fragments as attribute values are not supported. The reason for this change is that it would be confusing whether Markdown would work.

Incorrect:

<welcome name=<>Venus</> />
<welcome name=<span>Pluto</span> />

Correct:

<welcome name='Mars' />
Greater than (>) and right curly brace (})

JSX does not allow U+003E GREATER THAN (>) or U+007D RIGHT CURLY BRACE (}) literally in text, they need to be encoded as character references (or expressions). There is no good reason for this (some JSX parsers agree with us and don’t crash either). Therefore, in MDX, U+003E GREATER THAN (>) and U+007D RIGHT CURLY BRACE (}) are fine literally and don’t need to be encoded.

Syntax

This extensions support MDX both agnostic and gnostic to JavaScript. The first is agnostic to the programming language (it could contain attribute expressions and attribute value expressions with Rust or so), the last is specific to JavaScript (in which case attribute expressions must be spread expressions). To turn on gnostic mode, pass acorn.

The syntax of JSX supported here is described in W3C Backus–Naur form with the following additions:

  1. A - B — matches any string that matches A but does not match B.
  2. 'string' — same as "string" but with single quotes.
  3. BREAK — lookahead match for a block break opportunity (either EOF (end of file), U+000A LINE FEED (LF), U+000D CARRIAGE RETURN (CR), or another JSX tag)

The syntax is defined as follows, however, do note that interleaving (mixing) of markdown and MDX is defined elsewhere, and that the constraints are imposed in mdast-util-mdx-jsx.

; Entries
mdxFlow ::= *spaceOrTab element *spaceOrTab BREAK
mdxText ::= element

element ::= selfClosing | closed
selfClosing ::=
  ; constraint: tag MUST be named, MUST NOT be closing, and MUST be self-closing
  tag
closed ::=
  ; constraint: tag MUST NOT be closing and MUST NOT be self-closing
  tag
  *data
  ; constraint: tag MUST be closing, MUST NOT be self-closing, MUST NOT have
  ; attributes, and either both tags MUST have the same name or both tags MUST
  ; be nameless
  tag

data ::= element | text

; constraint: markdown whitespace (spaceOrTab | '\r' | '\n') is NOT
; allowed directly after `<` in order to allow `1 < 3` in markdown.
tag ::=
  '<' *1closing
  *1(*whitespace name *1attributesAfterIdentifier *1closing)
  *whitespace '>'

attributesAfterIdentifier ::=
  1*whitespace (attributesBoolean | attributesValue) |
  *whitespace attributesExpression |
attributesAfterValue ::=
  *whitespace (attributesBoolean | attributesExpression | attributesValue)
attributesBoolean ::= key *1attributesAfterIdentifier
; Note: in gnostic mode the value of the expression must instead be a single valid ES spread
; expression
attributesExpression ::= expression *1attributesAfterValue
attributesValue ::= key initializer *1attributesAfterValue

closing ::= *whitespace '/'

name ::= identifier *1(local | members)
key ::= identifier *1local
local ::= *whitespace ':' *whitespace identifier
members ::= member *member
member ::= *whitespace '.' *whitespace identifier

identifier ::= identifierStart *identifierPart
initializer ::= *whitespace '=' *whitespace value
value ::= doubleQuoted | singleQuoted | expression
; Note: in gnostic mode the value must instead be a single valid ES expression
expression ::= '{' *(expressionText | expression) '}'

doubleQuoted ::= '"' *doubleQuotedText '"'
singleQuoted ::= "'" *singleQuotedText "'"

spaceOrTab ::= ' ' | '\t'
text ::= character - '<' - '{'
whitespace ::= esWhitespace
doubleQuotedText ::= character - '"'
singleQuotedText ::= character - "'"
expressionText ::= character - '{' - '}'
identifierStart ::= esIdentifierStart
identifierPart ::= esIdentifierPart | '-'

; Unicode
; Any unicode code point
character ::=

; ECMAScript
; See “IdentifierStart”: <https://tc39.es/ecma262/#prod-IdentifierStart>
esIdentifierStart ::=
; See “IdentifierPart”: <https://tc39.es/ecma262/#prod-IdentifierPart>
esIdentifierPart ::=
; See “Whitespace”: <https://tc39.es/ecma262/#prod-WhiteSpace>
esWhitespace ::=

Errors

In gnostic mode, expressions are parsed with micromark-extension-mdx-expression, which throws some other errors.

Unexpected end of file $at, expected $expect

This error occurs for many different reasons if something was opened but not closed (source: micromark-extension-mdx-jsx, rule id: unexpected-eof).

Some examples are:

<
</
<a
<a:
<a.
<a b
<a b:
<a b=
<a b="
<a b='
<a b={
<a/

Unexpected character $at, expected $expect

This error occurs for many different reasons if an unexpected character is seen (source: micromark-extension-mdx-jsx, rule id: unexpected-character).

Some examples are:

<.>
</.>
<a?>
<a:+>
<a./>
<a b!>
<a b:1>
<a b=>
<a/->

Tokens

Many tokens are used:

Types

This package is fully typed with TypeScript. It exports the additional type Options.

Compatibility

This package is at least compatible with all maintained versions of Node.js. As of now, that is Node.js 12.20+, 14.14+, 16.0+, and 18.0+. It also works in Deno and modern browsers.

Security

This package deals with compiling JavaScript. If you do not trust the JavaScript, this package does nothing to change that.

Contribute

See contributing.md in micromark/.github for ways to get started. See support.md 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.

License

MIT © Titus Wormer