Socket
Socket
Sign inDemoInstall

micromark-extension-mdx-jsx

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

micromark-extension-mdx-jsx - npm Package Compare versions

Comparing version 1.0.3 to 1.0.4

19

dev/lib/factory-tag.d.ts

@@ -6,6 +6,6 @@ /**

* @param {State} nok
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* @param {boolean|undefined} allowLazy
* @param {Acorn | undefined} acorn
* @param {AcornOptions | undefined} acornOptions
* @param {boolean | undefined} addResult
* @param {boolean | undefined} allowLazy
* @param {string} tagType

@@ -38,2 +38,3 @@ * @param {string} tagMarkerType

export function factoryTag(
this: import('micromark-util-types').TokenizeContext,
effects: Effects,

@@ -74,10 +75,8 @@ ok: State,

) => void | import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
export type Effects = import('micromark-util-types').Effects
export type State = import('micromark-util-types').State
export type Code = import('micromark-util-types').Code
export type Point = import('micromark-util-types').Point
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Code = import('micromark-util-types').Code
export type Effects = import('micromark-util-types').Effects
export type State = import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
/**
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Effects} Effects
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Point} Point
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
*/
import {ok as assert} from 'uvu/assert'
import {start as idStart, cont as idCont} from 'estree-util-is-identifier-name'
import {factoryMdxExpression} from 'micromark-factory-mdx-expression'
import {factorySpace} from 'micromark-factory-space'
import {

@@ -25,6 +21,5 @@ markdownLineEnding,

import {types} from 'micromark-util-symbol/types.js'
import {ok as assert} from 'uvu/assert'
import {VFileMessage} from 'vfile-message'
const lazyLineEnd = {tokenize: tokenizeLazyLineEnd, partial: true}
/**

@@ -35,6 +30,6 @@ * @this {TokenizeContext}

* @param {State} nok
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* @param {boolean|undefined} allowLazy
* @param {Acorn | undefined} acorn
* @param {AcornOptions | undefined} acornOptions
* @param {boolean | undefined} addResult
* @param {boolean | undefined} allowLazy
* @param {string} tagType

@@ -104,13 +99,19 @@ * @param {string} tagMarkerType

let returnState
/** @type {NonNullable<Code>|undefined} */
/** @type {NonNullable<Code> | undefined} */
let marker
/** @type {Point|undefined} */
let startPoint
return start
/** @type {State} */
/**
* Start of MDX: JSX.
*
* ```markdown
* > | a <B /> c
* ^
* ```
*
* @type {State}
*/
function start(code) {
assert(code === codes.lessThan, 'expected `<`')
startPoint = self.now()
effects.enter(tagType)

@@ -120,10 +121,19 @@ effects.enter(tagMarkerType)

effects.exit(tagMarkerType)
return afterStart
return startAfter
}
/** @type {State} */
function afterStart(code) {
/**
* After `<`.
*
* ```markdown
* > | a <B /> c
* ^
* ```
*
* @type {State}
*/
function startAfter(code) {
// Deviate from JSX, which allows arbitrary whitespace.
// See: <https://github.com/micromark/micromark-extension-mdx-jsx/issues/7>.
if (markdownLineEnding(code) || markdownSpace(code)) {
if (markdownLineEndingOrSpace(code)) {
return nok(code)

@@ -133,9 +143,21 @@ }

// Any other ES whitespace does not get this treatment.
returnState = beforeName
return optionalEsWhitespace(code)
returnState = nameBefore
return esWhitespaceStart(code)
}
// Right after `<`, before an optional name.
/** @type {State} */
function beforeName(code) {
/**
* Before name, self slash, or end of tag for fragments.
*
* ```markdown
* > | a <B> c
* ^
* > | a </B> c
* ^
* > | a <> b
* ^
* ```
*
* @type {State}
*/
function nameBefore(code) {
// Closing tag.

@@ -146,4 +168,4 @@ if (code === codes.slash) {

effects.exit(tagClosingMarkerType)
returnState = beforeClosingTagName
return optionalEsWhitespace
returnState = closingTagNameBefore
return esWhitespaceStart
}

@@ -174,5 +196,15 @@

// At the start of a closing tag, right after `</`.
/** @type {State} */
function beforeClosingTagName(code) {
/**
* Before name of closing tag or end of closing fragment tag.
*
* ```markdown
* > | a </> b
* ^
* > | a </B> c
* ^
* ```
*
* @type {State}
*/
function closingTagNameBefore(code) {
// Fragment closing tag.

@@ -201,4 +233,12 @@ if (code === codes.greaterThan) {

// Inside the primary name.
/** @type {State} */
/**
* In primary name.
*
* ```markdown
* > | a <Bc> d
* ^
* ```
*
* @type {State}
*/
function primaryName(code) {

@@ -222,4 +262,4 @@ // Continuation of name: remain.

effects.exit(tagNamePrimaryType)
returnState = afterPrimaryName
return optionalEsWhitespace(code)
returnState = primaryNameAfter
return esWhitespaceStart(code)
}

@@ -237,5 +277,15 @@

// After a name.
/** @type {State} */
function afterPrimaryName(code) {
/**
* After primary name.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b:c> d
* ^
* ```
*
* @type {State}
*/
function primaryNameAfter(code) {
// Start of a member name.

@@ -246,4 +296,4 @@ if (code === codes.dot) {

effects.exit(tagNameMemberMarkerType)
returnState = beforeMemberName
return optionalEsWhitespace
returnState = memberNameBefore
return esWhitespaceStart
}

@@ -256,4 +306,4 @@

effects.exit(tagNamePrefixMarkerType)
returnState = beforeLocalName
return optionalEsWhitespace
returnState = localNameBefore
return esWhitespaceStart
}

@@ -269,3 +319,3 @@

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}

@@ -280,5 +330,13 @@

// We’ve seen a `.` and are expecting a member name.
/** @type {State} */
function beforeMemberName(code) {
/**
* Before member name.
*
* ```markdown
* > | a <b.c> d
* ^
* ```
*
* @type {State}
*/
function memberNameBefore(code) {
// Start of a member name.

@@ -298,6 +356,14 @@ if (code !== codes.eof && idStart(code)) {

// Inside the member name.
/** @type {State} */
/**
* In member name.
*
* ```markdown
* > | a <b.cd> e
* ^
* ```
*
* @type {State}
*/
function memberName(code) {
// Continuation of member name: stay in state
// Continuation of name: remain.
if (code === codes.dash || (code !== codes.eof && idCont(code))) {

@@ -308,3 +374,4 @@ effects.consume(code)

// End of member name (note that namespaces and members can’t be combined).
// End of name.
// Note: no `:` allowed here.
if (

@@ -319,4 +386,4 @@ code === codes.dot ||

effects.exit(tagNameMemberType)
returnState = afterMemberName
return optionalEsWhitespace(code)
returnState = memberNameAfter
return esWhitespaceStart(code)
}

@@ -334,6 +401,15 @@

// After a member name: this is the same as `afterPrimaryName` but we don’t
// expect colons.
/** @type {State} */
function afterMemberName(code) {
/**
* After member name.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b.c.d> e
* ^
* ```
*
* @type {State}
*/
function memberNameAfter(code) {
// Start another member name.

@@ -344,4 +420,4 @@ if (code === codes.dot) {

effects.exit(tagNameMemberMarkerType)
returnState = beforeMemberName
return optionalEsWhitespace
returnState = memberNameBefore
return esWhitespaceStart
}

@@ -357,3 +433,3 @@

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}

@@ -368,5 +444,13 @@

// We’ve seen a `:`, and are expecting a local name.
/** @type {State} */
function beforeLocalName(code) {
/**
* Local member name.
*
* ```markdown
* > | a <b:c> d
* ^
* ```
*
* @type {State}
*/
function localNameBefore(code) {
// Start of a local name.

@@ -392,6 +476,14 @@ if (code !== codes.eof && idStart(code)) {

// Inside the local name.
/** @type {State} */
/**
* In local name.
*
* ```markdown
* > | a <b:cd> e
* ^
* ```
*
* @type {State}
*/
function localName(code) {
// Continuation of local name: stay in state
// Continuation of name: remain.
if (code === codes.dash || (code !== codes.eof && idCont(code))) {

@@ -411,4 +503,4 @@ effects.consume(code)

effects.exit(tagNameLocalType)
returnState = afterLocalName
return optionalEsWhitespace(code)
returnState = localNameAfter
return esWhitespaceStart(code)
}

@@ -423,6 +515,18 @@

// After a local name: this is the same as `afterPrimaryName` but we don’t
// expect colons or periods.
/** @type {State} */
function afterLocalName(code) {
/**
* After local name.
*
* This is like as `primary_name_after`, but we don’t expect colons or
* periods.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b.c.d> e
* ^
* ```
*
* @type {State}
*/
function localNameAfter(code) {
// End of name.

@@ -436,3 +540,3 @@ if (

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}

@@ -447,5 +551,20 @@

/** @type {State} */
function beforeAttribute(code) {
// Mark as self-closing.
/**
* Before attribute.
*
* ```markdown
* > | a <b /> c
* ^
* > | a <b > c
* ^
* > | a <b {...c}> d
* ^
* > | a <b c> d
* ^
* ```
*
* @type {State}
*/
function attributeBefore(code) {
// Self-closing.
if (code === codes.slash) {

@@ -456,3 +575,3 @@ effects.enter(tagSelfClosingMarker)

returnState = selfClosing
return optionalEsWhitespace
return esWhitespaceStart
}

@@ -467,7 +586,6 @@

if (code === codes.leftCurlyBrace) {
assert(startPoint, 'expected `startPoint` to be defined')
return factoryMdxExpression.call(
self,
effects,
afterAttributeExpression,
attributeExpressionAfter,
tagExpressionAttributeType,

@@ -481,4 +599,3 @@ tagExpressionAttributeMarkerType,

false,
allowLazy,
startPoint.column
allowLazy
)(code)

@@ -503,13 +620,33 @@ }

// At the start of an attribute expression.
/** @type {State} */
function afterAttributeExpression(code) {
returnState = beforeAttribute
return optionalEsWhitespace(code)
/**
* After attribute expression.
*
* ```markdown
* > | a <b {c} d/> e
* ^
* ```
*
* @type {State}
*/
function attributeExpressionAfter(code) {
returnState = attributeBefore
return esWhitespaceStart(code)
}
// In the attribute name.
/** @type {State} */
/**
* In primary attribute name.
*
* ```markdown
* > | a <b cd/> e
* ^
* > | a <b c:d> e
* ^
* > | a <b c=d> e
* ^
* ```
*
* @type {State}
*/
function attributePrimaryName(code) {
// Continuation of the attribute name.
// Continuation of name: remain.
if (code === codes.dash || (code !== codes.eof && idCont(code))) {

@@ -531,4 +668,4 @@ effects.consume(code)

effects.exit(tagAttributeNamePrimaryType)
returnState = afterAttributePrimaryName
return optionalEsWhitespace(code)
returnState = attributePrimaryNameAfter
return esWhitespaceStart(code)
}

@@ -543,5 +680,17 @@

// After an attribute name, probably finding an equals.
/** @type {State} */
function afterAttributePrimaryName(code) {
/**
* After primary attribute name.
*
* ```markdown
* > | a <b c/> d
* ^
* > | a <b c:d> e
* ^
* > | a <b c=d> e
* ^
* ```
*
* @type {State}
*/
function attributePrimaryNameAfter(code) {
// Start of a local name.

@@ -552,7 +701,7 @@ if (code === codes.colon) {

effects.exit(tagAttributeNamePrefixMarkerType)
returnState = beforeAttributeLocalName
return optionalEsWhitespace
returnState = attributeLocalNameBefore
return esWhitespaceStart
}
// Start of an attribute value.
// Initializer: start of an attribute value.
if (code === codes.equalsTo) {

@@ -563,4 +712,4 @@ effects.exit(tagAttributeNameType)

effects.exit(tagAttributeInitializerMarkerType)
returnState = beforeAttributeValue
return optionalEsWhitespace
returnState = attributeValueBefore
return esWhitespaceStart
}

@@ -579,4 +728,4 @@

effects.exit(tagAttributeType)
returnState = beforeAttribute
return optionalEsWhitespace(code)
returnState = attributeBefore
return esWhitespaceStart(code)
}

@@ -591,5 +740,13 @@

// We’ve seen a `:`, and are expecting a local name.
/** @type {State} */
function beforeAttributeLocalName(code) {
/**
* Before local attribute name.
*
* ```markdown
* > | a <b c:d/> e
* ^
* ```
*
* @type {State}
*/
function attributeLocalNameBefore(code) {
// Start of a local name.

@@ -609,6 +766,16 @@ if (code !== codes.eof && idStart(code)) {

// In the local attribute name.
/** @type {State} */
/**
* In local attribute name.
*
* ```markdown
* > | a <b c:de/> f
* ^
* > | a <b c:d=e/> f
* ^
* ```
*
* @type {State}
*/
function attributeLocalName(code) {
// Continuation of the local attribute name.
// Continuation of name: remain.
if (code === codes.dash || (code !== codes.eof && idCont(code))) {

@@ -619,3 +786,3 @@ effects.consume(code)

// End of tag / attribute name.
// End of local name (note that we don’t expect another colon).
if (

@@ -631,4 +798,4 @@ code === codes.slash ||

effects.exit(tagAttributeNameType)
returnState = afterAttributeLocalName
return optionalEsWhitespace(code)
returnState = attributeLocalNameAfter
return esWhitespaceStart(code)
}

@@ -643,5 +810,15 @@

// After a local attribute name, expecting an equals.
/** @type {State} */
function afterAttributeLocalName(code) {
/**
* After local attribute name.
*
* ```markdown
* > | a <b c:d/> f
* ^
* > | a <b c:d=e/> f
* ^
* ```
*
* @type {State}
*/
function attributeLocalNameAfter(code) {
// Start of an attribute value.

@@ -652,7 +829,7 @@ if (code === codes.equalsTo) {

effects.exit(tagAttributeInitializerMarkerType)
returnState = beforeAttributeValue
return optionalEsWhitespace
returnState = attributeValueBefore
return esWhitespaceStart
}
// End of tag / new attribute.
// End of name.
if (

@@ -665,3 +842,3 @@ code === codes.slash ||

effects.exit(tagAttributeType)
return beforeAttribute(code)
return attributeBefore(code)
}

@@ -676,5 +853,15 @@

// After an attribute value initializer, expecting quotes and such.
/** @type {State} */
function beforeAttributeValue(code) {
/**
* After `=`, before value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* > | a <b c={d}/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueBefore(code) {
// Start of double- or single quoted value.

@@ -690,9 +877,8 @@ if (code === codes.quotationMark || code === codes.apostrophe) {

// Start of an assignment expression.
// Attribute value expression.
if (code === codes.leftCurlyBrace) {
assert(startPoint, 'expected `startPoint` to be defined')
return factoryMdxExpression.call(
self,
effects,
afterAttributeValueExpression,
attributeValueExpressionAfter,
tagAttributeValueExpressionType,

@@ -706,4 +892,3 @@ tagAttributeValueExpressionMarkerType,

false,
allowLazy,
startPoint.column
allowLazy
)(code)

@@ -722,11 +907,28 @@ }

/** @type {State} */
function afterAttributeValueExpression(code) {
/**
* After attribute value expression.
*
* ```markdown
* > | a <b c={d} e/> f
* ^
* ```
*
* @type {State}
*/
function attributeValueExpressionAfter(code) {
effects.exit(tagAttributeType)
returnState = beforeAttribute
return optionalEsWhitespace(code)
returnState = attributeBefore
return esWhitespaceStart(code)
}
// At the start of a quoted attribute value.
/** @type {State} */
/**
* Before quoted literal attribute value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueQuotedStart(code) {

@@ -739,3 +941,3 @@ assert(marker !== undefined, 'expected `marker` to be defined')

'in attribute value',
'a corresponding closing quote `' + String.fromCharCode(marker) + '`'
'a corresponding closing quote `' + String.fromCodePoint(marker) + '`'
)

@@ -751,4 +953,4 @@ }

marker = undefined
returnState = beforeAttribute
return optionalEsWhitespace
returnState = attributeBefore
return esWhitespaceStart
}

@@ -758,3 +960,3 @@

returnState = attributeValueQuotedStart
return optionalEsWhitespace(code)
return esWhitespaceStart(code)
}

@@ -766,4 +968,12 @@

// In a quoted attribute value.
/** @type {State} */
/**
* In quoted literal attribute value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueQuoted(code) {

@@ -775,3 +985,2 @@ if (code === codes.eof || code === marker || markdownLineEnding(code)) {

// Continuation.
effects.consume(code)

@@ -781,6 +990,13 @@ return attributeValueQuoted

// Right after the slash on a tag, e.g., `<asd /`.
/** @type {State} */
/**
* After self-closing slash.
*
* ```markdown
* > | a <b/> c
* ^
* ```
*
* @type {State}
*/
function selfClosing(code) {
// End of tag.
if (code === codes.greaterThan) {

@@ -800,4 +1016,12 @@ return tagEnd(code)

// At a `>`.
/** @type {State} */
/**
* At final `>`.
*
* ```markdown
* > | a <b> c
* ^
* ```
*
* @type {State}
*/
function tagEnd(code) {

@@ -812,28 +1036,18 @@ assert(code === codes.greaterThan, 'expected `>`')

// Optionally start whitespace.
/** @type {State} */
function optionalEsWhitespace(code) {
/**
* Before optional ECMAScript whitespace.
*
* ```markdown
* > | a <a b> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceStart(code) {
if (markdownLineEnding(code)) {
if (allowLazy) {
effects.enter(types.lineEnding)
effects.consume(code)
effects.exit(types.lineEnding)
return factorySpace(
effects,
optionalEsWhitespace,
types.linePrefix,
constants.tabSize
)
}
return effects.attempt(
lazyLineEnd,
factorySpace(
effects,
optionalEsWhitespace,
types.linePrefix,
constants.tabSize
),
crashEol
)(code)
effects.enter(types.lineEnding)
effects.consume(code)
effects.exit(types.lineEnding)
return esWhitespaceEolAfter
}

@@ -843,3 +1057,3 @@

effects.enter('esWhitespace')
return optionalEsWhitespaceContinue(code)
return esWhitespaceInside(code)
}

@@ -850,28 +1064,53 @@

// Continue optional whitespace.
/** @type {State} */
function optionalEsWhitespaceContinue(code) {
if (
markdownLineEnding(code) ||
!(markdownSpace(code) || unicodeWhitespace(code))
) {
/**
* In ECMAScript whitespace.
*
* ```markdown
* > | a <a b> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceInside(code) {
if (markdownLineEnding(code)) {
effects.exit('esWhitespace')
return optionalEsWhitespace(code)
return esWhitespaceStart(code)
}
effects.consume(code)
return optionalEsWhitespaceContinue
if (markdownSpace(code) || unicodeWhitespace(code)) {
effects.consume(code)
return esWhitespaceInside
}
effects.exit('esWhitespace')
return returnState(code)
}
/** @type {State} */
function crashEol() {
throw new VFileMessage(
'Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc',
self.now(),
'micromark-extension-mdx-jsx:unexpected-eof'
)
/**
* After eol in whitespace.
*
* ```markdown
* > | a <a\nb> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceEolAfter(code) {
// Lazy continuation in a flow tag is a syntax error.
if (!allowLazy && self.parser.lazy[self.now().line]) {
throw new VFileMessage(
'Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc',
self.now(),
'micromark-extension-mdx-jsx:unexpected-eof'
)
}
return esWhitespaceStart(code)
}
// Crash at a nonconforming character.
/**
* Crash at a nonconforming character.
*
* @param {Code} code

@@ -887,3 +1126,5 @@ * @param {string} at

: 'character `' +
(code === codes.graveAccent ? '` ` `' : String.fromCharCode(code)) +
(code === codes.graveAccent
? '` ` `'
: String.fromCodePoint(code)) +
'` (' +

@@ -903,23 +1144,2 @@ serializeCharCode(code) +

/** @type {Tokenizer} */
function tokenizeLazyLineEnd(effects, ok, nok) {
const self = this
return start
/** @type {State} */
function start(code) {
assert(markdownLineEnding(code), 'expected eol')
effects.enter(types.lineEnding)
effects.consume(code)
effects.exit(types.lineEnding)
return lineStart
}
/** @type {State} */
function lineStart(code) {
return self.parser.lazy[self.now().line] ? nok(code) : ok(code)
}
}
/**

@@ -926,0 +1146,0 @@ * @param {NonNullable<Code>} code

/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (flow).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -12,7 +18,8 @@ export function jsxFlow(

): Construct
export type Construct = import('micromark-util-types').Construct
export type Tokenizer = import('micromark-util-types').Tokenizer
export type State = import('micromark-util-types').State
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Construct = import('micromark-util-types').Construct
export type State = import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
/**
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
*/
import {ok as assert} from 'uvu/assert'
import {markdownLineEnding, markdownSpace} from 'micromark-util-character'
import {factorySpace} from 'micromark-factory-space'
import {markdownLineEnding} from 'micromark-util-character'
import {codes} from 'micromark-util-symbol/codes.js'
import {types} from 'micromark-util-symbol/types.js'
import {ok as assert} from 'uvu/assert'
import {factoryTag} from './factory-tag.js'
/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (flow).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -25,3 +32,13 @@ export function jsxFlow(acorn, acornOptions, addResult) {

/** @type {Tokenizer} */
/**
* MDX JSX (flow).
*
* ```markdown
* > | <A />
* ^^^^^
* ```
*
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeJsxFlow(effects, ok, nok) {

@@ -32,9 +49,34 @@ const self = this

/** @type {State} */
/**
* Start of MDX: JSX (flow).
*
* ```markdown
* > | <A />
* ^
* ```
*
* @type {State}
*/
function start(code) {
// To do: in `markdown-rs`, constructs need to parse the indent themselves.
// This should also be introduced in `micromark-js`.
assert(code === codes.lessThan, 'expected `<`')
return before(code)
}
/**
* After optional whitespace, before of MDX JSX (flow).
*
* ```markdown
* > | <A />
* ^
* ```
*
* @type {State}
*/
function before(code) {
return factoryTag.call(
self,
effects,
factorySpace(effects, after, types.whitespace),
after,
nok,

@@ -73,4 +115,29 @@ acorn,

/** @type {State} */
/**
* After an MDX JSX (flow) tag.
*
* ```markdown
* > | <A>
* ^
* ```
*
* @type {State}
*/
function after(code) {
return markdownSpace(code)
? factorySpace(effects, end, types.whitespace)(code)
: end(code)
}
/**
* After an MDX JSX (flow) tag, after optional whitespace.
*
* ```markdown
* > | <A> <B>
* ^
* ```
*
* @type {State}
*/
function end(code) {
// Another tag.

@@ -77,0 +144,0 @@ return code === codes.lessThan

/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (text).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -12,6 +18,7 @@ export function jsxText(

): Construct
export type Construct = import('micromark-util-types').Construct
export type Tokenizer = import('micromark-util-types').Tokenizer
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Construct = import('micromark-util-types').Construct
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
/**
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
*/

@@ -11,6 +12,12 @@

/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (text).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -20,3 +27,13 @@ export function jsxText(acorn, acornOptions, addResult) {

/** @type {Tokenizer} */
/**
* MDX JSX (text).
*
* ```markdown
* > | a <b />.
* ^^^^^
* ```
*
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeJsxText(effects, ok, nok) {

@@ -23,0 +40,0 @@ return factoryTag.call(

/**
* @param {Options} [options]
* Create an extension for `micromark` to enable MDX JSX syntax.
*
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {Extension}
* Extension for `micromark` that can be passed in `extensions` to enable MDX
* JSX syntax.
*/
export function mdxJsx(options?: Options | undefined): Extension
export function mdxJsx(options?: Options | null | undefined): Extension
export type Extension = import('micromark-util-types').Extension

@@ -10,6 +15,21 @@ export type Acorn = import('micromark-factory-mdx-expression').Acorn

import('micromark-factory-mdx-expression').AcornOptions
/**
* Configuration (optional).
*/
export type Options = {
addResult?: boolean | undefined
acorn?: import('micromark-util-events-to-acorn').Acorn | undefined
acornOptions?: import('acorn').Options | undefined
/**
* Acorn parser to use (optional).
*/
acorn?: Acorn | null | undefined
/**
* Configuration for acorn (default: `{ecmaVersion: 2020, locations: true,
* sourceType: 'module'}`).
*
* All fields except `locations` can be set.
*/
acornOptions?: AcornOptions | null | undefined
/**
* Whether to add `estree` fields to tokens with results from acorn.
*/
addResult?: boolean | null | undefined
}

@@ -9,5 +9,12 @@ /**

* @typedef Options
* @property {boolean} [addResult=false]
* @property {Acorn} [acorn]
* @property {AcornOptions} [acornOptions]
* Configuration (optional).
* @property {Acorn | null | undefined} [acorn]
* Acorn parser to use (optional).
* @property {AcornOptions | null | undefined} [acornOptions]
* Configuration for acorn (default: `{ecmaVersion: 2020, locations: true,
* sourceType: 'module'}`).
*
* All fields except `locations` can be set.
* @property {boolean | null | undefined} [addResult=false]
* Whether to add `estree` fields to tokens with results from acorn.
*/

@@ -20,8 +27,14 @@

/**
* @param {Options} [options]
* Create an extension for `micromark` to enable MDX JSX syntax.
*
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {Extension}
* Extension for `micromark` that can be passed in `extensions` to enable MDX
* JSX syntax.
*/
export function mdxJsx(options = {}) {
const acorn = options.acorn
/** @type {AcornOptions|undefined} */
export function mdxJsx(options) {
const settings = options || {}
const acorn = settings.acorn
/** @type {AcornOptions | undefined} */
let acornOptions

@@ -38,6 +51,6 @@

{ecmaVersion: 2020, sourceType: 'module'},
options.acornOptions,
settings.acornOptions,
{locations: true}
)
} else if (options.acornOptions || options.addResult) {
} else if (settings.acornOptions || settings.addResult) {
throw new Error('Expected an `acorn` instance passed in as `options.acorn`')

@@ -47,5 +60,17 @@ }

return {
flow: {[codes.lessThan]: jsxFlow(acorn, acornOptions, options.addResult)},
text: {[codes.lessThan]: jsxText(acorn, acornOptions, options.addResult)}
flow: {
[codes.lessThan]: jsxFlow(
acorn || undefined,
acornOptions,
settings.addResult || false
)
},
text: {
[codes.lessThan]: jsxText(
acorn || undefined,
acornOptions,
settings.addResult || false
)
}
}
}
/**
* @typedef {import('./lib/syntax.js').Options} Options
*/
export {mdxJsx} from './lib/syntax.js'

@@ -6,6 +6,6 @@ /**

* @param {State} nok
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* @param {boolean|undefined} allowLazy
* @param {Acorn | undefined} acorn
* @param {AcornOptions | undefined} acornOptions
* @param {boolean | undefined} addResult
* @param {boolean | undefined} allowLazy
* @param {string} tagType

@@ -38,2 +38,3 @@ * @param {string} tagMarkerType

export function factoryTag(
this: import('micromark-util-types').TokenizeContext,
effects: Effects,

@@ -74,10 +75,8 @@ ok: State,

) => void | import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
export type Effects = import('micromark-util-types').Effects
export type State = import('micromark-util-types').State
export type Code = import('micromark-util-types').Code
export type Point = import('micromark-util-types').Point
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Code = import('micromark-util-types').Code
export type Effects = import('micromark-util-types').Effects
export type State = import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
/**
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Effects} Effects
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Point} Point
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
*/
import {start as idStart, cont as idCont} from 'estree-util-is-identifier-name'
import {factoryMdxExpression} from 'micromark-factory-mdx-expression'
import {factorySpace} from 'micromark-factory-space'
import {

@@ -21,6 +19,3 @@ markdownLineEnding,

import {VFileMessage} from 'vfile-message'
const lazyLineEnd = {
tokenize: tokenizeLazyLineEnd,
partial: true
}
/**

@@ -31,6 +26,6 @@ * @this {TokenizeContext}

* @param {State} nok
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* @param {boolean|undefined} allowLazy
* @param {Acorn | undefined} acorn
* @param {AcornOptions | undefined} acornOptions
* @param {boolean | undefined} addResult
* @param {boolean | undefined} allowLazy
* @param {string} tagType

@@ -63,3 +58,2 @@ * @param {string} tagMarkerType

// eslint-disable-next-line max-params
export function factoryTag(

@@ -101,15 +95,18 @@ effects,

/** @type {State} */
let returnState
/** @type {NonNullable<Code>|undefined} */
/** @type {NonNullable<Code> | undefined} */
let marker
/** @type {Point|undefined} */
let startPoint
return start
/** @type {State} */
/**
* Start of MDX: JSX.
*
* ```markdown
* > | a <B /> c
* ^
* ```
*
* @type {State}
*/
function start(code) {
startPoint = self.now()
effects.enter(tagType)

@@ -119,20 +116,42 @@ effects.enter(tagMarkerType)

effects.exit(tagMarkerType)
return afterStart
return startAfter
}
/** @type {State} */
function afterStart(code) {
/**
* After `<`.
*
* ```markdown
* > | a <B /> c
* ^
* ```
*
* @type {State}
*/
function startAfter(code) {
// Deviate from JSX, which allows arbitrary whitespace.
// See: <https://github.com/micromark/micromark-extension-mdx-jsx/issues/7>.
if (markdownLineEnding(code) || markdownSpace(code)) {
if (markdownLineEndingOrSpace(code)) {
return nok(code)
} // Any other ES whitespace does not get this treatment.
}
returnState = beforeName
return optionalEsWhitespace(code)
} // Right after `<`, before an optional name.
// Any other ES whitespace does not get this treatment.
returnState = nameBefore
return esWhitespaceStart(code)
}
/** @type {State} */
function beforeName(code) {
/**
* Before name, self slash, or end of tag for fragments.
*
* ```markdown
* > | a <B> c
* ^
* > | a </B> c
* ^
* > | a <> b
* ^
* ```
*
* @type {State}
*/
function nameBefore(code) {
// Closing tag.

@@ -143,10 +162,12 @@ if (code === 47) {

effects.exit(tagClosingMarkerType)
returnState = beforeClosingTagName
return optionalEsWhitespace
} // Fragment opening tag.
returnState = closingTagNameBefore
return esWhitespaceStart
}
// Fragment opening tag.
if (code === 62) {
return tagEnd(code)
} // Start of a name.
}
// Start of a name.
if (code !== null && idStart(code)) {

@@ -158,3 +179,2 @@ effects.enter(tagNameType)

}
crash(

@@ -168,12 +188,23 @@ code,

)
} // At the start of a closing tag, right after `</`.
}
/** @type {State} */
function beforeClosingTagName(code) {
/**
* Before name of closing tag or end of closing fragment tag.
*
* ```markdown
* > | a </> b
* ^
* > | a </B> c
* ^
* ```
*
* @type {State}
*/
function closingTagNameBefore(code) {
// Fragment closing tag.
if (code === 62) {
return tagEnd(code)
} // Start of a closing tag name.
}
// Start of a closing tag name.
if (code !== null && idStart(code)) {

@@ -185,3 +216,2 @@ effects.enter(tagNameType)

}
crash(

@@ -195,6 +225,14 @@ code,

)
} // Inside the primary name.
}
/** @type {State} */
/**
* In primary name.
*
* ```markdown
* > | a <Bc> d
* ^
* ```
*
* @type {State}
*/
function primaryName(code) {

@@ -205,4 +243,5 @@ // Continuation of name: remain.

return primaryName
} // End of name.
}
// End of name.
if (

@@ -218,6 +257,5 @@ code === 46 ||

effects.exit(tagNamePrimaryType)
returnState = afterPrimaryName
return optionalEsWhitespace(code)
returnState = primaryNameAfter
return esWhitespaceStart(code)
}
crash(

@@ -231,7 +269,17 @@ code,

)
} // After a name.
}
/** @type {State} */
function afterPrimaryName(code) {
/**
* After primary name.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b:c> d
* ^
* ```
*
* @type {State}
*/
function primaryNameAfter(code) {
// Start of a member name.

@@ -242,6 +290,7 @@ if (code === 46) {

effects.exit(tagNameMemberMarkerType)
returnState = beforeMemberName
return optionalEsWhitespace
} // Start of a local name.
returnState = memberNameBefore
return esWhitespaceStart
}
// Start of a local name.
if (code === 58) {

@@ -251,6 +300,7 @@ effects.enter(tagNamePrefixMarkerType)

effects.exit(tagNamePrefixMarkerType)
returnState = beforeLocalName
return optionalEsWhitespace
} // End of name.
returnState = localNameBefore
return esWhitespaceStart
}
// End of name.
if (

@@ -263,5 +313,4 @@ code === 47 ||

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}
crash(

@@ -272,7 +321,15 @@ code,

)
} // We’ve seen a `.` and are expecting a member name.
}
/** @type {State} */
function beforeMemberName(code) {
/**
* Before member name.
*
* ```markdown
* > | a <b.c> d
* ^
* ```
*
* @type {State}
*/
function memberNameBefore(code) {
// Start of a member name.

@@ -284,3 +341,2 @@ if (code !== null && idStart(code)) {

}
crash(

@@ -291,13 +347,23 @@ code,

)
} // Inside the member name.
}
/** @type {State} */
/**
* In member name.
*
* ```markdown
* > | a <b.cd> e
* ^
* ```
*
* @type {State}
*/
function memberName(code) {
// Continuation of member name: stay in state
// Continuation of name: remain.
if (code === 45 || (code !== null && idCont(code))) {
effects.consume(code)
return memberName
} // End of member name (note that namespaces and members can’t be combined).
}
// End of name.
// Note: no `:` allowed here.
if (

@@ -312,6 +378,5 @@ code === 46 ||

effects.exit(tagNameMemberType)
returnState = afterMemberName
return optionalEsWhitespace(code)
returnState = memberNameAfter
return esWhitespaceStart(code)
}
crash(

@@ -325,8 +390,17 @@ code,

)
} // After a member name: this is the same as `afterPrimaryName` but we don’t
// expect colons.
}
/** @type {State} */
function afterMemberName(code) {
/**
* After member name.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b.c.d> e
* ^
* ```
*
* @type {State}
*/
function memberNameAfter(code) {
// Start another member name.

@@ -337,6 +411,7 @@ if (code === 46) {

effects.exit(tagNameMemberMarkerType)
returnState = beforeMemberName
return optionalEsWhitespace
} // End of name.
returnState = memberNameBefore
return esWhitespaceStart
}
// End of name.
if (

@@ -349,5 +424,4 @@ code === 47 ||

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}
crash(

@@ -358,7 +432,15 @@ code,

)
} // We’ve seen a `:`, and are expecting a local name.
}
/** @type {State} */
function beforeLocalName(code) {
/**
* Local member name.
*
* ```markdown
* > | a <b:c> d
* ^
* ```
*
* @type {State}
*/
function localNameBefore(code) {
// Start of a local name.

@@ -370,3 +452,2 @@ if (code !== null && idStart(code)) {

}
crash(

@@ -376,18 +457,27 @@ code,

'a character that can start a name, such as a letter, `$`, or `_`' +
(code === 43 || (code !== null && code > 46 && code < 58)
? /* `/` - `9` */
' (note: to create a link in MDX, use `[text](url)`)'
(code === 43 ||
(code !== null && code > 46 && code < 58) /* `/` - `9` */
? ' (note: to create a link in MDX, use `[text](url)`)'
: '')
)
} // Inside the local name.
}
/** @type {State} */
/**
* In local name.
*
* ```markdown
* > | a <b:cd> e
* ^
* ```
*
* @type {State}
*/
function localName(code) {
// Continuation of local name: stay in state
// Continuation of name: remain.
if (code === 45 || (code !== null && idCont(code))) {
effects.consume(code)
return localName
} // End of local name (note that we don’t expect another colon, or a member).
}
// End of local name (note that we don’t expect another colon, or a member).
if (

@@ -401,6 +491,5 @@ code === 47 ||

effects.exit(tagNameLocalType)
returnState = afterLocalName
return optionalEsWhitespace(code)
returnState = localNameAfter
return esWhitespaceStart(code)
}
crash(

@@ -411,8 +500,20 @@ code,

)
} // After a local name: this is the same as `afterPrimaryName` but we don’t
// expect colons or periods.
}
/** @type {State} */
function afterLocalName(code) {
/**
* After local name.
*
* This is like as `primary_name_after`, but we don’t expect colons or
* periods.
*
* ```markdown
* > | a <b.c> d
* ^
* > | a <b.c.d> e
* ^
* ```
*
* @type {State}
*/
function localNameAfter(code) {
// End of name.

@@ -426,5 +527,4 @@ if (

effects.exit(tagNameType)
return beforeAttribute(code)
return attributeBefore(code)
}
crash(

@@ -436,6 +536,21 @@ code,

}
/** @type {State} */
function beforeAttribute(code) {
// Mark as self-closing.
/**
* Before attribute.
*
* ```markdown
* > | a <b /> c
* ^
* > | a <b > c
* ^
* > | a <b {...c}> d
* ^
* > | a <b c> d
* ^
* ```
*
* @type {State}
*/
function attributeBefore(code) {
// Self-closing.
if (code === 47) {

@@ -446,9 +561,11 @@ effects.enter(tagSelfClosingMarker)

returnState = selfClosing
return optionalEsWhitespace
} // End of tag.
return esWhitespaceStart
}
// End of tag.
if (code === 62) {
return tagEnd(code)
} // Attribute expression.
}
// Attribute expression.
if (code === 123) {

@@ -458,3 +575,3 @@ return factoryMdxExpression.call(

effects,
afterAttributeExpression,
attributeExpressionAfter,
tagExpressionAttributeType,

@@ -468,7 +585,7 @@ tagExpressionAttributeMarkerType,

false,
allowLazy,
startPoint.column
allowLazy
)(code)
} // Start of an attribute name.
}
// Start of an attribute name.
if (code !== null && idStart(code)) {

@@ -481,3 +598,2 @@ effects.enter(tagAttributeType)

}
crash(

@@ -488,20 +604,41 @@ code,

)
} // At the start of an attribute expression.
}
/** @type {State} */
/**
* After attribute expression.
*
* ```markdown
* > | a <b {c} d/> e
* ^
* ```
*
* @type {State}
*/
function attributeExpressionAfter(code) {
returnState = attributeBefore
return esWhitespaceStart(code)
}
function afterAttributeExpression(code) {
returnState = beforeAttribute
return optionalEsWhitespace(code)
} // In the attribute name.
/** @type {State} */
/**
* In primary attribute name.
*
* ```markdown
* > | a <b cd/> e
* ^
* > | a <b c:d> e
* ^
* > | a <b c=d> e
* ^
* ```
*
* @type {State}
*/
function attributePrimaryName(code) {
// Continuation of the attribute name.
// Continuation of name: remain.
if (code === 45 || (code !== null && idCont(code))) {
effects.consume(code)
return attributePrimaryName
} // End of attribute name or tag.
}
// End of attribute name or tag.
if (

@@ -517,6 +654,5 @@ code === 47 ||

effects.exit(tagAttributeNamePrimaryType)
returnState = afterAttributePrimaryName
return optionalEsWhitespace(code)
returnState = attributePrimaryNameAfter
return esWhitespaceStart(code)
}
crash(

@@ -527,7 +663,19 @@ code,

)
} // After an attribute name, probably finding an equals.
}
/** @type {State} */
function afterAttributePrimaryName(code) {
/**
* After primary attribute name.
*
* ```markdown
* > | a <b c/> d
* ^
* > | a <b c:d> e
* ^
* > | a <b c=d> e
* ^
* ```
*
* @type {State}
*/
function attributePrimaryNameAfter(code) {
// Start of a local name.

@@ -538,6 +686,7 @@ if (code === 58) {

effects.exit(tagAttributeNamePrefixMarkerType)
returnState = beforeAttributeLocalName
return optionalEsWhitespace
} // Start of an attribute value.
returnState = attributeLocalNameBefore
return esWhitespaceStart
}
// Initializer: start of an attribute value.
if (code === 61) {

@@ -548,6 +697,7 @@ effects.exit(tagAttributeNameType)

effects.exit(tagAttributeInitializerMarkerType)
returnState = beforeAttributeValue
return optionalEsWhitespace
} // End of tag / new attribute.
returnState = attributeValueBefore
return esWhitespaceStart
}
// End of tag / new attribute.
if (

@@ -563,6 +713,5 @@ code === 47 ||

effects.exit(tagAttributeType)
returnState = beforeAttribute
return optionalEsWhitespace(code)
returnState = attributeBefore
return esWhitespaceStart(code)
}
crash(

@@ -573,7 +722,15 @@ code,

)
} // We’ve seen a `:`, and are expecting a local name.
}
/** @type {State} */
function beforeAttributeLocalName(code) {
/**
* Before local attribute name.
*
* ```markdown
* > | a <b c:d/> e
* ^
* ```
*
* @type {State}
*/
function attributeLocalNameBefore(code) {
// Start of a local name.

@@ -585,3 +742,2 @@ if (code !== null && idStart(code)) {

}
crash(

@@ -592,13 +748,24 @@ code,

)
} // In the local attribute name.
}
/** @type {State} */
/**
* In local attribute name.
*
* ```markdown
* > | a <b c:de/> f
* ^
* > | a <b c:d=e/> f
* ^
* ```
*
* @type {State}
*/
function attributeLocalName(code) {
// Continuation of the local attribute name.
// Continuation of name: remain.
if (code === 45 || (code !== null && idCont(code))) {
effects.consume(code)
return attributeLocalName
} // End of tag / attribute name.
}
// End of local name (note that we don’t expect another colon).
if (

@@ -614,6 +781,5 @@ code === 47 ||

effects.exit(tagAttributeNameType)
returnState = afterAttributeLocalName
return optionalEsWhitespace(code)
returnState = attributeLocalNameAfter
return esWhitespaceStart(code)
}
crash(

@@ -624,7 +790,17 @@ code,

)
} // After a local attribute name, expecting an equals.
}
/** @type {State} */
function afterAttributeLocalName(code) {
/**
* After local attribute name.
*
* ```markdown
* > | a <b c:d/> f
* ^
* > | a <b c:d=e/> f
* ^
* ```
*
* @type {State}
*/
function attributeLocalNameAfter(code) {
// Start of an attribute value.

@@ -635,6 +811,7 @@ if (code === 61) {

effects.exit(tagAttributeInitializerMarkerType)
returnState = beforeAttributeValue
return optionalEsWhitespace
} // End of tag / new attribute.
returnState = attributeValueBefore
return esWhitespaceStart
}
// End of name.
if (

@@ -647,5 +824,4 @@ code === 47 ||

effects.exit(tagAttributeType)
return beforeAttribute(code)
return attributeBefore(code)
}
crash(

@@ -656,7 +832,17 @@ code,

)
} // After an attribute value initializer, expecting quotes and such.
}
/** @type {State} */
function beforeAttributeValue(code) {
/**
* After `=`, before value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* > | a <b c={d}/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueBefore(code) {
// Start of double- or single quoted value.

@@ -670,4 +856,5 @@ if (code === 34 || code === 39) {

return attributeValueQuotedStart
} // Start of an assignment expression.
}
// Attribute value expression.
if (code === 123) {

@@ -677,3 +864,3 @@ return factoryMdxExpression.call(

effects,
afterAttributeValueExpression,
attributeValueExpressionAfter,
tagAttributeValueExpressionType,

@@ -687,7 +874,5 @@ tagAttributeValueExpressionMarkerType,

false,
allowLazy,
startPoint.column
allowLazy
)(code)
}
crash(

@@ -702,12 +887,29 @@ code,

}
/** @type {State} */
function afterAttributeValueExpression(code) {
/**
* After attribute value expression.
*
* ```markdown
* > | a <b c={d} e/> f
* ^
* ```
*
* @type {State}
*/
function attributeValueExpressionAfter(code) {
effects.exit(tagAttributeType)
returnState = beforeAttribute
return optionalEsWhitespace(code)
} // At the start of a quoted attribute value.
returnState = attributeBefore
return esWhitespaceStart(code)
}
/** @type {State} */
/**
* Before quoted literal attribute value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueQuotedStart(code) {

@@ -718,6 +920,5 @@ if (code === null) {

'in attribute value',
'a corresponding closing quote `' + String.fromCharCode(marker) + '`'
'a corresponding closing quote `' + String.fromCodePoint(marker) + '`'
)
}
if (code === marker) {

@@ -730,17 +931,23 @@ effects.enter(tagAttributeValueLiteralMarkerType)

marker = undefined
returnState = beforeAttribute
return optionalEsWhitespace
returnState = attributeBefore
return esWhitespaceStart
}
if (markdownLineEnding(code)) {
returnState = attributeValueQuotedStart
return optionalEsWhitespace(code)
return esWhitespaceStart(code)
}
effects.enter(tagAttributeValueLiteralValueType)
return attributeValueQuoted(code)
} // In a quoted attribute value.
}
/** @type {State} */
/**
* In quoted literal attribute value.
*
* ```markdown
* > | a <b c="d"/> e
* ^
* ```
*
* @type {State}
*/
function attributeValueQuoted(code) {

@@ -750,16 +957,21 @@ if (code === null || code === marker || markdownLineEnding(code)) {

return attributeValueQuotedStart(code)
} // Continuation.
}
effects.consume(code)
return attributeValueQuoted
} // Right after the slash on a tag, e.g., `<asd /`.
}
/** @type {State} */
/**
* After self-closing slash.
*
* ```markdown
* > | a <b/> c
* ^
* ```
*
* @type {State}
*/
function selfClosing(code) {
// End of tag.
if (code === 62) {
return tagEnd(code)
}
crash(

@@ -773,6 +985,14 @@ code,

)
} // At a `>`.
}
/** @type {State} */
/**
* At final `>`.
*
* ```markdown
* > | a <b> c
* ^
* ```
*
* @type {State}
*/
function tagEnd(code) {

@@ -784,55 +1004,76 @@ effects.enter(tagMarkerType)

return ok
} // Optionally start whitespace.
}
/** @type {State} */
function optionalEsWhitespace(code) {
/**
* Before optional ECMAScript whitespace.
*
* ```markdown
* > | a <a b> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceStart(code) {
if (markdownLineEnding(code)) {
if (allowLazy) {
effects.enter('lineEnding')
effects.consume(code)
effects.exit('lineEnding')
return factorySpace(effects, optionalEsWhitespace, 'linePrefix', 4)
}
return effects.attempt(
lazyLineEnd,
factorySpace(effects, optionalEsWhitespace, 'linePrefix', 4),
crashEol
)(code)
effects.enter('lineEnding')
effects.consume(code)
effects.exit('lineEnding')
return esWhitespaceEolAfter
}
if (markdownSpace(code) || unicodeWhitespace(code)) {
effects.enter('esWhitespace')
return optionalEsWhitespaceContinue(code)
return esWhitespaceInside(code)
}
return returnState(code)
} // Continue optional whitespace.
}
/** @type {State} */
function optionalEsWhitespaceContinue(code) {
if (
markdownLineEnding(code) ||
!(markdownSpace(code) || unicodeWhitespace(code))
) {
/**
* In ECMAScript whitespace.
*
* ```markdown
* > | a <a b> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceInside(code) {
if (markdownLineEnding(code)) {
effects.exit('esWhitespace')
return optionalEsWhitespace(code)
return esWhitespaceStart(code)
}
if (markdownSpace(code) || unicodeWhitespace(code)) {
effects.consume(code)
return esWhitespaceInside
}
effects.exit('esWhitespace')
return returnState(code)
}
effects.consume(code)
return optionalEsWhitespaceContinue
/**
* After eol in whitespace.
*
* ```markdown
* > | a <a\nb> c
* ^
* ```
*
* @type {State}
*/
function esWhitespaceEolAfter(code) {
// Lazy continuation in a flow tag is a syntax error.
if (!allowLazy && self.parser.lazy[self.now().line]) {
throw new VFileMessage(
'Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc',
self.now(),
'micromark-extension-mdx-jsx:unexpected-eof'
)
}
return esWhitespaceStart(code)
}
/** @type {State} */
function crashEol() {
throw new VFileMessage(
'Unexpected lazy line in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc',
self.now(),
'micromark-extension-mdx-jsx:unexpected-eof'
)
} // Crash at a nonconforming character.
/**
* Crash at a nonconforming character.
*
* @param {Code} code

@@ -842,3 +1083,2 @@ * @param {string} at

*/
function crash(code, at, expect) {

@@ -850,3 +1090,3 @@ throw new VFileMessage(

: 'character `' +
(code === 96 ? '` ` `' : String.fromCharCode(code)) +
(code === 96 ? '` ` `' : String.fromCodePoint(code)) +
'` (' +

@@ -865,21 +1105,3 @@ serializeCharCode(code) +

}
/** @type {Tokenizer} */
function tokenizeLazyLineEnd(effects, ok, nok) {
const self = this
return start
/** @type {State} */
function start(code) {
effects.enter('lineEnding')
effects.consume(code)
effects.exit('lineEnding')
return lineStart
}
/** @type {State} */
function lineStart(code) {
return self.parser.lazy[self.now().line] ? nok(code) : ok(code)
}
}
/**

@@ -889,5 +1111,4 @@ * @param {NonNullable<Code>} code

*/
function serializeCharCode(code) {
return 'U+' + code.toString(16).toUpperCase().padStart(4, '0')
}
/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (flow).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -12,7 +18,8 @@ export function jsxFlow(

): Construct
export type Construct = import('micromark-util-types').Construct
export type Tokenizer = import('micromark-util-types').Tokenizer
export type State = import('micromark-util-types').State
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Construct = import('micromark-util-types').Construct
export type State = import('micromark-util-types').State
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
/**
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
*/
import {markdownLineEnding, markdownSpace} from 'micromark-util-character'
import {factorySpace} from 'micromark-factory-space'
import {markdownLineEnding} from 'micromark-util-character'
import {factoryTag} from './factory-tag.js'
/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (flow).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/
export function jsxFlow(acorn, acornOptions, addResult) {

@@ -23,14 +31,50 @@ return {

}
/** @type {Tokenizer} */
/**
* MDX JSX (flow).
*
* ```markdown
* > | <A />
* ^^^^^
* ```
*
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeJsxFlow(effects, ok, nok) {
const self = this
return start
/** @type {State} */
/**
* Start of MDX: JSX (flow).
*
* ```markdown
* > | <A />
* ^
* ```
*
* @type {State}
*/
function start(code) {
// To do: in `markdown-rs`, constructs need to parse the indent themselves.
// This should also be introduced in `micromark-js`.
return before(code)
}
/**
* After optional whitespace, before of MDX JSX (flow).
*
* ```markdown
* > | <A />
* ^
* ```
*
* @type {State}
*/
function before(code) {
return factoryTag.call(
self,
effects,
factorySpace(effects, after, 'whitespace'),
after,
nok,

@@ -68,5 +112,30 @@ acorn,

}
/** @type {State} */
/**
* After an MDX JSX (flow) tag.
*
* ```markdown
* > | <A>
* ^
* ```
*
* @type {State}
*/
function after(code) {
return markdownSpace(code)
? factorySpace(effects, end, 'whitespace')(code)
: end(code)
}
/**
* After an MDX JSX (flow) tag, after optional whitespace.
*
* ```markdown
* > | <A> <B>
* ^
* ```
*
* @type {State}
*/
function end(code) {
// Another tag.

@@ -73,0 +142,0 @@ return code === 60

/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (text).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/

@@ -12,6 +18,7 @@ export function jsxText(

): Construct
export type Construct = import('micromark-util-types').Construct
export type Tokenizer = import('micromark-util-types').Tokenizer
export type Acorn = import('micromark-factory-mdx-expression').Acorn
export type AcornOptions =
import('micromark-factory-mdx-expression').AcornOptions
export type Construct = import('micromark-util-types').Construct
export type TokenizeContext = import('micromark-util-types').TokenizeContext
export type Tokenizer = import('micromark-util-types').Tokenizer
/**
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
* @typedef {import('micromark-util-types').Tokenizer} Tokenizer
* @typedef {import('micromark-factory-mdx-expression').Acorn} Acorn
* @typedef {import('micromark-factory-mdx-expression').AcornOptions} AcornOptions
*/
import {factoryTag} from './factory-tag.js'
/**
* @param {Acorn|undefined} acorn
* @param {AcornOptions|undefined} acornOptions
* @param {boolean|undefined} addResult
* Parse JSX (text).
*
* @param {Acorn | undefined} acorn
* Acorn parser to use (optional).
* @param {AcornOptions | undefined} acornOptions
* Configuration for acorn.
* @param {boolean | undefined} addResult
* Whether to add `estree` fields to tokens with results from acorn.
* @returns {Construct}
* Construct.
*/
export function jsxText(acorn, acornOptions, addResult) {

@@ -19,4 +27,14 @@ return {

}
/** @type {Tokenizer} */
/**
* MDX JSX (text).
*
* ```markdown
* > | a <b />.
* ^^^^^
* ```
*
* @this {TokenizeContext}
* @type {Tokenizer}
*/
function tokenizeJsxText(effects, ok, nok) {

@@ -23,0 +41,0 @@ return factoryTag.call(

/**
* @param {Options} [options]
* Create an extension for `micromark` to enable MDX JSX syntax.
*
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {Extension}
* Extension for `micromark` that can be passed in `extensions` to enable MDX
* JSX syntax.
*/
export function mdxJsx(options?: Options | undefined): Extension
export function mdxJsx(options?: Options | null | undefined): Extension
export type Extension = import('micromark-util-types').Extension

@@ -10,6 +15,21 @@ export type Acorn = import('micromark-factory-mdx-expression').Acorn

import('micromark-factory-mdx-expression').AcornOptions
/**
* Configuration (optional).
*/
export type Options = {
addResult?: boolean | undefined
acorn?: import('micromark-util-events-to-acorn').Acorn | undefined
acornOptions?: import('acorn').Options | undefined
/**
* Acorn parser to use (optional).
*/
acorn?: Acorn | null | undefined
/**
* Configuration for acorn (default: `{ecmaVersion: 2020, locations: true,
* sourceType: 'module'}`).
*
* All fields except `locations` can be set.
*/
acornOptions?: AcornOptions | null | undefined
/**
* Whether to add `estree` fields to tokens with results from acorn.
*/
addResult?: boolean | null | undefined
}

@@ -9,19 +9,31 @@ /**

* @typedef Options
* @property {boolean} [addResult=false]
* @property {Acorn} [acorn]
* @property {AcornOptions} [acornOptions]
* Configuration (optional).
* @property {Acorn | null | undefined} [acorn]
* Acorn parser to use (optional).
* @property {AcornOptions | null | undefined} [acornOptions]
* Configuration for acorn (default: `{ecmaVersion: 2020, locations: true,
* sourceType: 'module'}`).
*
* All fields except `locations` can be set.
* @property {boolean | null | undefined} [addResult=false]
* Whether to add `estree` fields to tokens with results from acorn.
*/
import {jsxText} from './jsx-text.js'
import {jsxFlow} from './jsx-flow.js'
/**
* @param {Options} [options]
* Create an extension for `micromark` to enable MDX JSX syntax.
*
* @param {Options | null | undefined} [options]
* Configuration (optional).
* @returns {Extension}
* Extension for `micromark` that can be passed in `extensions` to enable MDX
* JSX syntax.
*/
export function mdxJsx(options = {}) {
const acorn = options.acorn
/** @type {AcornOptions|undefined} */
export function mdxJsx(options) {
const settings = options || {}
const acorn = settings.acorn
/** @type {AcornOptions | undefined} */
let acornOptions
if (acorn) {

@@ -33,3 +45,2 @@ if (!acorn.parse || !acorn.parseExpressionAt) {

}
acornOptions = Object.assign(

@@ -40,3 +51,3 @@ {

},
options.acornOptions,
settings.acornOptions,
{

@@ -46,14 +57,21 @@ locations: true

)
} else if (options.acornOptions || options.addResult) {
} else if (settings.acornOptions || settings.addResult) {
throw new Error('Expected an `acorn` instance passed in as `options.acorn`')
}
return {
flow: {
[60]: jsxFlow(acorn, acornOptions, options.addResult)
[60]: jsxFlow(
acorn || undefined,
acornOptions,
settings.addResult || false
)
},
text: {
[60]: jsxText(acorn, acornOptions, options.addResult)
[60]: jsxText(
acorn || undefined,
acornOptions,
settings.addResult || false
)
}
}
}
{
"name": "micromark-extension-mdx-jsx",
"version": "1.0.3",
"version": "1.0.4",
"description": "micromark extension to support MDX or MDX.js JSX",

@@ -55,7 +55,10 @@ "license": "MIT",

"devDependencies": {
"@types/estree": "^1.0.0",
"@types/estree-jsx": "^1.0.0",
"@types/mdast": "^3.0.0",
"@types/tape": "^4.0.0",
"@types/node": "^20.0.0",
"acorn": "^8.0.0",
"acorn-jsx": "^5.0.0",
"c8": "^7.0.0",
"estree-util-visit": "^1.0.0",
"mdast-zone": "^5.0.0",

@@ -65,15 +68,16 @@ "micromark": "^3.0.0",

"prettier": "^2.0.0",
"remark-cli": "^10.0.0",
"remark-cli": "^11.0.0",
"remark-preset-wooorm": "^9.0.0",
"rimraf": "^3.0.0",
"tape": "^5.0.0",
"type-coverage": "^2.0.0",
"typescript": "^4.0.0",
"xo": "^0.47.0"
"typescript": "^5.0.0",
"xo": "^0.54.0"
},
"scripts": {
"build": "rimraf \"dev/**/*.d.ts\" \"script/**/*.d.ts\" \"test/**/*.d.ts\" && tsc && type-coverage && micromark-build",
"prepack": "npm run build && npm run format",
"build": "tsc --build --clean && tsc --build && type-coverage && micromark-build",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node --conditions development test/index.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node --conditions development test/index.js",
"test-api-prod": "node --conditions production test/index.js",
"test-api-dev": "node --conditions development test/index.js",
"test-api": "npm run test-api-dev && npm run test-api-prod",
"test-coverage": "c8 --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"

@@ -92,5 +96,4 @@ },

"rules": {
"node/file-extension-in-import": "off",
"unicorn/no-this-assignment": "off",
"unicorn/prefer-code-point": "off"
"n/file-extension-in-import": "off",
"unicorn/no-this-assignment": "off"
}

@@ -97,0 +100,0 @@ },

@@ -11,25 +11,58 @@ # micromark-extension-mdx-jsx

**[micromark][]** extension to support [MDX][mdx-js] (or MDX.js) JSX.
[micromark][] extension to support [MDX][mdxjs] JSX (`<Component />`).
This package provides the low-level modules for integrating with the micromark
tokenizer but has no handling of compiling to HTML: go to a syntax tree instead.
## Contents
* [What is this?](#what-is-this)
* [When to use this](#when-to-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`mdxJsx(options?)`](#mdxjsxoptions)
* [`Options`](#options)
* [Authoring](#authoring)
* [Syntax](#syntax)
* [Errors](#errors)
* [Unexpected end of file $at, expected $expect](#unexpected-end-of-file-at-expected-expect)
* [Unexpected character $at, expected $expect](#unexpected-character-at-expected-expect)
* [Tokens](#tokens)
* [Types](#types)
* [Compatibility](#compatibility)
* [Security](#security)
* [Related](#related)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package contains an extension that adds support for the JSX syntax enabled
by [MDX][mdxjs] to [`micromark`][micromark].
These extensions are used inside MDX.
It mostly matches how JSX works in most places that support it (TypeScript,
Babel, esbuild, SWC, etc).
This package can be made aware or unaware of JavaScript syntax.
When unaware, expressions could include Rust or variables or whatnot.
## When to use this
This package is already included in [xdm][] and [`mdx-js/mdx` (next)][mdx-js].
This project is useful when you want to support JSX in markdown.
You should probably use [`micromark-extension-mdx`][mdx] or
[`micromark-extension-mdxjs`][mdxjs] instead, which combine this package with
other MDX features.
Alternatively, if you’re using [`micromark`][micromark] or
[`mdast-util-from-markdown`][from-markdown] and you don’t want all of MDX, use
this package.
You can use this extension when you are working with [`micromark`][micromark].
To support all MDX features, use
[`micromark-extension-mdxjs`][micromark-extension-mdxjs] instead.
When you need a syntax tree, combine this package with
[`mdast-util-mdx-jsx`][mdast-util-mdx-jsx].
All these packages are used in [`remark-mdx`][remark-mdx], which focusses on
making it easier to transform content by abstracting these internals away.
When you are using [`mdx-js/mdx`][mdxjs], all of this is already included.
## Install
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
This package is [ESM only][esm].
In Node.js (version 16+), install with [npm][]:
[npm][]:
```sh

@@ -39,2 +72,16 @@ npm install micromark-extension-mdx-jsx

In Deno with [`esm.sh`][esmsh]:
```js
import {mdxJsx} from 'https://esm.sh/micromark-extension-mdx-jsx@1'
```
In browsers with [`esm.sh`][esmsh]:
```html
<script type="module">
import {mdxJsx} from 'https://esm.sh/micromark-extension-mdx-jsx@1?bundle'
</script>
```
## Use

@@ -57,13 +104,12 @@

…which is rather useless: go to a syntax tree with
[`mdast-util-from-markdown`][from-markdown] and
[`mdast-util-mdx-expression`][util] instead.
…which is useless: go to a syntax tree with
[`mdast-util-from-markdown`][mdast-util-from-markdown] and
[`mdast-util-mdx-jsx`][mdast-util-mdx-jsx] instead.
## API
This package exports the following identifiers: `mdxJsx`.
This package exports the identifier [`mdxJsx`][api-mdx-jsx].
There is no default export.
The export map supports the endorsed
[`development` condition](https://nodejs.org/api/packages.html#packages_resolving_user_conditions).
The export map supports the [`development` condition][development].
Run `node --conditions development module.js` to get instrumented dev code.

@@ -74,88 +120,120 @@ Without this condition, production code is loaded.

A function that can be called with options that returns an extension for
micromark to parse JSX (can be passed in `extensions`).
Create an extension for `micromark` to enable MDX JSX syntax.
##### `options`
###### Parameters
###### `options.acorn`
* `options` ([`Options`][api-options], optional)
— configuration
Acorn parser to use ([`Acorn`][acorn], optional).
###### Returns
###### `options.acornOptions`
Extension for `micromark` that can be passed in `extensions` to enable MDX
JSX syntax ([`Extension`][micromark-extension]).
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`
###### `options.addResult`
Configuration (TypeScript type).
Whether to add an `estree` field to the `mdxTextJsx` and `mdxFlowJsx` tokens
with the results from acorn (`boolean`, default: `false`).
###### Fields
## Syntax
* `acorn` ([`Acorn`][acorn], optional)
— acorn parser to use
* `acornOptions` ([`AcornOptions`][acorn-options], default:
`{ecmaVersion: 2020, locations: true, sourceType: 'module'}`)
— configuration for acorn; all fields except `locations` can be set
* `addResult` (`boolean`, default: `false`)
— whether to add `estree` fields to tokens with results from acorn
This extensions support both MDX and MDX.js.
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`.
## Authoring
The syntax of JSX supported here is described in [W3C Backus–Naur form][w3c-bnf]
with the following additions:
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][mdxjs-interleaving] on the
MDX site.
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)
###### Comments inside tags
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`][util].
JavaScript comments in JSX are not supported.
<!--grammar start-->
Incorrect:
<pre><code>; Entries
<a id=x-mdx-flow href=#x-mdx-flow>mdxFlow</a> ::= *<a href=#x-space-or-tab>spaceOrTab</a> <a href=#x-element>element</a> *<a href=#x-space-or-tab>spaceOrTab</a> BREAK
<a id=x-mdx-text href=#x-mdx-text>mdxText</a> ::= <a href=#x-element>element</a>
```jsx
<hi/*comment!*//>
<hello// comment!
/>
```
<a id=x-element href=#x-element>element</a> ::= <a href=#x-self-closing>selfClosing</a> | <a href=#x-closed>closed</a>
<a id=x-self-closing href=#x-self-closing>selfClosing</a> ::=
; constraint: tag MUST be named, MUST NOT be closing, and MUST be self-closing
<a href=#x-tag>tag</a>
<a id=x-closed href=#x-closed>closed</a> ::=
; constraint: tag MUST NOT be closing and MUST NOT be self-closing
<a href=#x-tag>tag</a>
*<a href=#x-data>data</a>
; 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
<a href=#x-tag>tag</a>
Correct:
<a id=x-data href=#x-data>data</a> ::= <a href=#x-element>element</a> | <a href=#x-text>text</a>
```jsx
<hi/>
<hello
/>
```
; constraint: markdown whitespace (<a href=#x-space-or-tab>spaceOrTab</a> | '\r' | '\n') is NOT
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:
```jsx
<welcome name=<>Venus</> />
<welcome name=<span>Pluto</span> />
```
Correct:
```jsx
<welcome name='Mars' />
<welcome name={<span>Jupiter</span>} />
```
###### 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
JSX forms with the following BNF:
<!--grammar start-->
<pre><code><a id=x-mdx-jsx-flow href=#x-mdx-jsx-flow>mdx_jsx_flow</a> ::= <a href=#x-mdx-jsx>mdx_jsx</a> *<a href=#x-space-or-tab>space_or_tab</a> [<a href=#x-mdx-jsx>mdx_jsx</a> *<a href=#x-space-or-tab>space_or_tab</a>]
<a id=x-mdx-jsx-text href=#x-mdx-jsx-text>mdx_jsx_text</a> ::= <a href=#x-mdx-jsx>mdx_jsx</a>
; constraint: markdown whitespace (`<a href=#x-space-or-tab>space_or_tab</a> | <a href=#x-eol>eol</a>`) is NOT
; allowed directly after `&lt;` in order to allow `1 &lt; 3` in markdown.
<a id=x-tag href=#x-tag>tag</a> ::=
'<' *1<a href=#x-closing>closing</a>
*1(*<a href=#x-whitespace>whitespace</a> <a href=#x-name>name</a> *1<a href=#x-attributes-after-identifier>attributesAfterIdentifier</a> *1<a href=#x-closing>closing</a>)
<a id=x-mdx-jsx href=#x-mdx-jsx>mdx_jsx</a> ::=
'&lt;' [<a href=#x-closing>closing</a>]
[*<a href=#x-whitespace>whitespace</a> <a href=#x-name>name</a> [<a href=#x-attributes-after-identifier>attributes_after_identifier</a>] [<a href=#x-closing>closing</a>]]
*<a href=#x-whitespace>whitespace</a> '>'
<a id=x-attributes-after-identifier href=#x-attributes-after-identifier>attributesAfterIdentifier</a> ::=
1*<a href=#x-whitespace>whitespace</a> (<a href=#x-attributes-boolean>attributesBoolean</a> | <a href=#x-attributes-value>attributesValue</a>) |
*<a href=#x-whitespace>whitespace</a> <a href=#x-attributes-expression>attributesExpression</a> |
<a id=x-attributes-after-value href=#x-attributes-after-value>attributesAfterValue</a> ::=
*<a href=#x-whitespace>whitespace</a> (<a href=#x-attributes-boolean>attributesBoolean</a> | <a href=#x-attributes-expression>attributesExpression</a> | <a href=#x-attributes-value>attributesValue</a>)
<a name=attributes-boolean href=#x-attributes-boolean>attributesBoolean</a> ::= <a href=#x-key>key</a> *1<a href=#x-attributes-after-identifier>attributesAfterIdentifier</a>
<a id=x-attributes-after-identifier href=#x-attributes-after-identifier>attributes_after_identifier</a> ::=
1*<a href=#x-whitespace>whitespace</a> (<a href=#x-attributes-boolean>attributes_boolean</a> | <a href=#x-attributes-value>attributes_value</a>) |
*<a href=#x-whitespace>whitespace</a> <a href=#x-attributes-expression>attributes_expression</a> |
<a id=x-attributes-after-value href=#x-attributes-after-value>attributes_after_value</a> ::=
*<a href=#x-whitespace>whitespace</a> (<a href=#x-attributes-boolean>attributes_boolean</a> | <a href=#x-attributes-expression>attributes_expression</a> | <a href=#x-attributes-value>attributes_value</a>)
<a id=x-attributes-boolean href=#x-attributes-boolean>attributes_boolean</a> ::= <a href=#x-key>key</a> [<a href=#x-attributes-after-identifier>attributes_after_identifier</a>]
; Note: in gnostic mode the value of the expression must instead be a single valid ES spread
; expression
<a name=attributes-expression href=#x-attributes-expression>attributesExpression</a> ::= <a href=#x-expression>expression</a> *1<a href=#x-attributes-after-value>attributesAfterValue</a>
<a name=attributes-value href=#x-attributes-value>attributesValue</a> ::= <a href=#x-key>key</a> <a href=#x-initializer>initializer</a> *1<a href=#x-attributes-after-value>attributesAfterValue</a>
<a id=x-attributes-expression href=#x-attributes-expression>attributes_expression</a> ::= <a href=#x-expression>expression</a> [<a href=#x-attributes-after-value>attributes_after_value</a>]
<a id=x-attributes-value href=#x-attributes-value>attributes_value</a> ::= <a href=#x-key>key</a> <a href=#x-initializer>initializer</a> [<a href=#x-attributes-after-value>attributes_after_value</a>]
<a id=x-closing href=#x-closing>closing</a> ::= *<a href=#x-whitespace>whitespace</a> '/'
<a id=x-name href=#x-name>name</a> ::= <a href=#x-identifier>identifier</a> *1(<a href=#x-local>local</a> | <a href=#x-members>members</a>)
<a id=x-key href=#x-key>key</a> ::= <a href=#x-identifier>identifier</a> *1<a href=#x-local>local</a>
<a id=x-name href=#x-name>name</a> ::= <a href=#x-identifier>identifier</a> [<a href=#x-local>local</a> | <a href=#x-members>members</a>]
<a id=x-key href=#x-key>key</a> ::= <a href=#x-identifier>identifier</a> [<a href=#x-local>local</a>]
<a id=x-local href=#x-local>local</a> ::= *<a href=#x-whitespace>whitespace</a> ':' *<a href=#x-whitespace>whitespace</a> <a href=#x-identifier>identifier</a>

@@ -165,31 +243,28 @@ <a id=x-members href=#x-members>members</a> ::= <a href=#x-member>member</a> *<a href=#x-member>member</a>

<a id=x-identifier href=#x-identifier>identifier</a> ::= <a href=#x-identifier-start>identifierStart</a> *<a href=#x-identifier-part>identifierPart</a>
<a id=x-identifier href=#x-identifier>identifier</a> ::= <a href=#x-identifier-start>identifier_start</a> *<a href=#x-identifier-part>identifier_part</a>
<a id=x-initializer href=#x-initializer>initializer</a> ::= *<a href=#x-whitespace>whitespace</a> '=' *<a href=#x-whitespace>whitespace</a> <a href=#x-value>value</a>
<a id=x-value href=#x-value>value</a> ::= <a href=#x-double-quoted>doubleQuoted</a> | <a href=#x-single-quoted>singleQuoted</a> | <a href=#x-expression>expression</a>
<a id=x-value href=#x-value>value</a> ::= <a href=#x-double-quoted>double_quoted</a> | <a href=#x-single-quoted>single_quoted</a> | <a href=#x-expression>expression</a>
; Note: in gnostic mode the value must instead be a single valid ES expression
<a id=x-expression href=#x-expression>expression</a> ::= '{' *(<a href=#x-expression-text>expressionText</a> | <a href=#x-expression>expression</a>) '}'
<a id=x-expression href=#x-expression>expression</a> ::= '{' *(<a href=#x-expression-text>expression_text</a> | <a href=#x-expression>expression</a>) '}'
<a id=x-double-quoted href=#x-double-quoted>doubleQuoted</a> ::= '"' *<a href=#x-double-quoted-text>doubleQuotedText</a> '"'
<a id=x-single-quoted href=#x-single-quoted>singleQuoted</a> ::= "'" *<a href=#x-single-quoted-text>singleQuotedText</a> "'"
<a id=x-double-quoted href=#x-double-quoted>double_quoted</a> ::= '"' *<a href=#x-double-quoted-text>double_quoted_text</a> '"'
<a id=x-single-quoted href=#x-single-quoted>single_quoted</a> ::= "'" *<a href=#x-single-quoted-text>single_quoted_text</a> "'"
<a id=x-space-or-tab href=#x-space-or-tab>spaceOrTab</a> ::= ' ' | '\t'
<a id=x-text href=#x-text>text</a> ::= <a href=#x-character>character</a> - '<' - '{'
<a id=x-whitespace href=#x-whitespace>whitespace</a> ::= <a href=#x-es-whitespace>esWhitespace</a>
<a id=x-double-quoted-text href=#x-double-quoted-text>doubleQuotedText</a> ::= <a href=#x-character>character</a> - '"'
<a id=x-single-quoted-text href=#x-single-quoted-text>singleQuotedText</a> ::= <a href=#x-character>character</a> - "'"
<a id=x-expression-text href=#x-expression-text>expressionText</a> ::= <a href=#x-character>character</a> - '{' - '}'
<a id=x-identifier-start href=#x-identifier-start>identifierStart</a> ::= <a href=#x-es-identifier-start>esIdentifierStart</a>
<a id=x-identifier-part href=#x-identifier-part>identifierPart</a> ::= <a href=#x-es-identifier-part>esIdentifierPart</a> | '-'
<a id=x-whitespace href=#x-whitespace>whitespace</a> ::= <a href=#x-es-whitespace>es_whitespace</a>
<a id=x-double-quoted-text href=#x-double-quoted-text>double_quoted_text</a> ::= char - '"'
<a id=x-single-quoted-text href=#x-single-quoted-text>single_quoted_text</a> ::= char - "'"
<a id=x-expression-text href=#x-expression-text>expression_text</a> ::= char - '{' - '}'
<a id=x-identifier-start href=#x-identifier-start>identifier_start</a> ::= <a href=#x-es-identifier-start>es_identifier_start</a>
<a id=x-identifier-part href=#x-identifier-part>identifier_part</a> ::= <a href=#x-es-identifier-part>es_identifier_part</a> | '-'
; Unicode
; Any unicode code point
<a id=x-character href=#x-character>character</a> ::=
<a id=x-space-or-tab href=#x-space-or-tab>space_or_tab</a> ::= '\t' | ' '
<a id=x-eol href=#x-eol>eol</a> ::= '\n' | '\r' | '\r\n'
; ECMAScript
; See “IdentifierStart”: &lt;<a href=https://tc39.es/ecma262/#prod-IdentifierStart>https://tc39.es/ecma262/#prod-IdentifierStart</a>>
<a id=x-es-identifier-start href=#x-es-identifier-start>esIdentifierStart</a> ::=
<a id=x-es-identifier-start href=#x-es-identifier-start>es_identifier_start</a> ::= ?
; See “IdentifierPart”: &lt;<a href=https://tc39.es/ecma262/#prod-IdentifierPart>https://tc39.es/ecma262/#prod-IdentifierPart</a>>
<a id=x-es-identifier-part href=#x-es-identifier-part>esIdentifierPart</a> ::=
<a id=x-es-identifier-part href=#x-es-identifier-part>es_identifier_part</a> ::= ?
; See “Whitespace”: &lt;<a href=https://tc39.es/ecma262/#prod-WhiteSpace>https://tc39.es/ecma262/#prod-WhiteSpace</a>>
<a id=x-es-whitespace href=#x-es-whitespace>esWhitespace</a> ::=
<a id=x-es-whitespace href=#x-es-whitespace>es_whitespace</a> ::= ?
</code></pre>

@@ -199,7 +274,27 @@

As the flow construct occurs in flow, like all flow constructs, it must be
followed by an eol (line ending) or eof (end of file).
The grammar for JSX in markdown is much stricter than that of HTML in
markdown.
The primary benefit of this is that tags are parsed into tokens, and thus
can be processed.
Another, arguable, benefit of this is that it comes with syntax errors: if
an author types something that is nonsensical, an error is thrown with
information about where it happened, what occurred, and what was expected
instead.
This extension supports expressions both aware and unaware to JavaScript
(respectively gnostic and agnostic).
Depending on whether acorn is passed, either valid JavaScript must be used in
expressions, or arbitrary text (such as Rust code or so) can be used.
More on this can be found in
[§ Syntax of `micromark-extension-mdx-expression`][expression-syntax].
## Errors
In gnostic mode, expressions are parsed with
[`micromark-extension-mdx-expression`][mdx-expression], which also throws
certain errors.
In aware (gnostic) mode, expressions are parsed with
[`micromark-extension-mdx-expression`][micromark-extension-mdx-expression],
which throws some more errors.

@@ -318,18 +413,28 @@ ### Unexpected end of file $at, expected $expect

## Types
This package is fully typed with [TypeScript][].
It exports the additional type [`Options`][api-options].
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 16+.
Our projects sometimes work with older versions, but this is not guaranteed.
These extensions work with `micromark` version 3+.
## Security
This package is safe.
## Related
* [`micromark/micromark`][micromark]
— the smallest commonmark-compliant markdown parser that exists
* [`micromark/micromark-extension-mdx`][mdx]
— micromark extension to support MDX
* [`micromark/micromark-extension-mdxjs`][mdxjs]
— micromark extension to support MDX.js
* [`micromark/micromark-extension-mdx-expression`][mdx-expression]
— micromark extension to support MDX (or MDX.js) expressions
* [`micromark/micromark-extension-mdx-md`][mdx-md]
— micromark extension to support misc MDX changes
* [`micromark/micromark-extension-mdxjs-esm`][mdxjs-esm]
— micromark extension to support MDX.js import/exports
* [`syntax-tree/mdast-util-mdx`][mdast-util-mdx]
— mdast utility to support MDX (or MDX.js)
* [`micromark-extension-mdxjs`][micromark-extension-mdxjs]
— support all MDX syntax
* [`mdast-util-mdx-jsx`][mdast-util-mdx-jsx]
— support MDX JSX in mdast
* [`remark-mdx`][remark-mdx]
— support all MDX syntax in remark

@@ -380,2 +485,4 @@ ## Contribute

[esmsh]: https://esm.sh
[license]: license

@@ -391,26 +498,34 @@

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[typescript]: https://www.typescriptlang.org
[development]: https://nodejs.org/api/packages.html#packages_resolving_user_conditions
[micromark]: https://github.com/micromark/micromark
[xdm]: https://github.com/wooorm/xdm
[micromark-extension]: https://github.com/micromark/micromark#syntaxextension
[mdx-js]: https://github.com/mdx-js/mdx
[micromark-extension-mdxjs]: https://github.com/micromark/micromark-extension-mdxjs
[mdx-expression]: https://github.com/micromark/micromark-extension-mdx-expression
[micromark-extension-mdx-expression]: https://github.com/micromark/micromark-extension-mdx-expression
[mdx-md]: https://github.com/micromark/micromark-extension-mdx-md
[expression-syntax]: https://github.com/micromark/micromark-extension-mdx-expression/blob/main/packages/micromark-extension-mdx-expression/readme.md#syntax
[mdxjs-esm]: https://github.com/micromark/micromark-extension-mdxjs-esm
[mdast-util-mdx-jsx]: https://github.com/syntax-tree/mdast-util-mdx-jsx
[mdx]: https://github.com/micromark/micromark-extension-mdx
[mdast-util-from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown
[mdxjs]: https://github.com/micromark/micromark-extension-mdxjs
[remark-mdx]: https://mdxjs.com/packages/remark-mdx/
[util]: https://github.com/syntax-tree/mdast-util-mdx-jsx
[mdxjs]: https://mdxjs.com
[mdast-util-mdx]: https://github.com/syntax-tree/mdast-util-mdx
[mdxjs-interleaving]: https://mdxjs.com/docs/what-is-mdx/#interleaving
[w3c-bnf]: https://www.w3.org/Notation.html
[acorn]: https://github.com/acornjs/acorn
[from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown
[acorn-options]: https://github.com/acornjs/acorn/blob/96c721dbf89d0ccc3a8c7f39e69ef2a6a3c04dfa/acorn/dist/acorn.d.ts#L16
[api-mdx-jsx]: #mdxjsxoptions
[api-options]: #options
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc