micromark-extension-mdx-jsx
micromark extension to support MDX JSX (<Component />
).
Contents
- What is this?
- When to use this
- Install
- Use
- API
- Authoring
- Syntax
- Errors
- Tokens
- Types
- Compatibility
- Security
- Related
- Contribute
- License
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:
A - B
— matches any string that matchesA
but does not matchB
.'string'
— same as"string"
but with single quotes.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:
mdxJsxFlowTag
for the whole JSX tag (<a>
)mdxJsxTextTag
^mdxJsxFlowTagMarker
for the tag markers (<
,>
)mdxJsxTextTagMarker
^mdxJsxFlowTagClosingMarker
for the/
marking a closing tag (</a>
)mdxJsxTextTagClosingMarker
^mdxJsxFlowTagSelfClosingMarker
for the/
marking a self-closing tag (<a/>
)mdxJsxTextTagSelfClosingMarker
^mdxJsxFlowTagName
for the whole tag name (a:b
in<a:b>
)mdxJsxTextTagName
^mdxJsxFlowTagNamePrimary
for the first name (a
in<a:b>
)mdxJsxTextTagNamePrimary
^mdxJsxFlowTagNameMemberMarker
for the.
marking in members (<a.b>
)mdxJsxTextTagNameMemberMarker
^mdxJsxFlowTagNameMember
for member names (b
in<a:b>
)mdxJsxTextTagNameMember
^mdxJsxFlowTagNamePrefixMarker
for the:
between primary and local (<a:b>
)mdxJsxTextTagNamePrefixMarker
^mdxJsxFlowTagNameLocal
for the local name (b
in<a:b>
)mdxJsxTextTagNameLocal
^mdxJsxFlowTagExpressionAttribute
for whole expression attributes (<a {...b}>
)mdxJsxTextTagExpressionAttribute
^mdxJsxFlowTagExpressionAttributeMarker
for{
,}
in expression attributesmdxJsxTextTagExpressionAttributeMarker
^mdxJsxFlowTagExpressionAttributeValue
for chunks of what’s inside expression attributesmdxJsxTextTagExpressionAttributeValue
^mdxJsxFlowTagAttribute
for a whole normal attribute (<a b>
)mdxJsxTextTagAttribute
^mdxJsxFlowTagAttributeName
for the whole name of an attribute (b:c
in<a b:c>
)mdxJsxTextTagAttributeName
^mdxJsxFlowTagAttributeNamePrimary
for the first name of an attribute (b
in<a b:c>
)mdxJsxTextTagAttributeNamePrimary
^mdxJsxFlowTagAttributeNamePrefixMarker
for the:
between primary and local (<a b:c>
)mdxJsxTextTagAttributeNamePrefixMarker
^mdxJsxFlowTagAttributeNameLocal
for the local name of an attribute (c
in<a b:c>
)mdxJsxTextTagAttributeNameLocal
^mdxJsxFlowTagAttributeInitializerMarker
for the=
between an attribute name and valuemdxJsxTextTagAttributeInitializerMarker
^mdxJsxFlowTagAttributeValueLiteral
for a string attribute value (<a b="">
)mdxJsxTextTagAttributeValueLiteral
^mdxJsxFlowTagAttributeValueLiteralMarker
for the quotes around a string attribute value ("
or'
)mdxJsxTextTagAttributeValueLiteralMarker
^mdxJsxFlowTagAttributeValueLiteralValue
for chunks of what’s inside string attribute valuesmdxJsxTextTagAttributeValueLiteralValue
^mdxJsxFlowTagAttributeValueExpression
for an expression attribute value (<a b={1}>
)mdxJsxTextTagAttributeValueExpression
^mdxJsxFlowTagAttributeValueExpressionMarker
for the{
and}
of expression attribute valuesmdxJsxTextTagAttributeValueExpressionMarker
^mdxJsxFlowTagAttributeValueExpressionValue
for chunks of what’s inside expression attribute valuesmdxJsxTextTagAttributeValueExpressionValue
^
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.
Related
micromark/micromark-extension-mdxjs
— micromark extension to support MDXsyntax-tree/mdast-util-mdx-jsx
— mdast utility to support MDX JSXremark-mdx
— remark plugin to support MDX syntax
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.