mdast-util-mdx-jsx
Advanced tools
Comparing version 1.2.0 to 2.0.0
@@ -1,50 +0,9 @@ | ||
/** @type {FromMarkdownExtension} */ | ||
export const mdxJsxFromMarkdown: FromMarkdownExtension | ||
/** @type {ToMarkdownExtension} */ | ||
export const mdxJsxToMarkdown: ToMarkdownExtension | ||
export type Literal = import('mdast').Literal | ||
export type Parent = import('mdast').Parent | ||
export type FromMarkdownExtension = import('mdast-util-from-markdown').Extension | ||
export type FromMarkdownHandle = import('mdast-util-from-markdown').Handle | ||
export type Token = import('mdast-util-from-markdown').Token | ||
export type ToMarkdownExtension = import('mdast-util-to-markdown').Options | ||
export type ToMarkdownHandle = import('mdast-util-to-markdown').Handle | ||
export type ToMarkdownMap = import('mdast-util-to-markdown').Map | ||
export type OnEnterError = import('mdast-util-from-markdown').OnEnterError | ||
export type OnExitError = import('mdast-util-from-markdown').OnExitError | ||
export type Program = import('estree-jsx').Program | ||
export type MdxJsxAttributeValueExpression = | ||
import('./complex-types').MdxJsxAttributeValueExpression | ||
export type MdxJsxAttribute = import('./complex-types').MdxJsxAttribute | ||
import('./lib/index.js').MdxJsxAttributeValueExpression | ||
export type MdxJsxAttribute = import('./lib/index.js').MdxJsxAttribute | ||
export type MdxJsxExpressionAttribute = | ||
import('./complex-types').MdxJsxExpressionAttribute | ||
export type MdxJsxFlowElement = import('./complex-types').MdxJsxFlowElement | ||
export type MdxJsxTextElement = import('./complex-types').MdxJsxTextElement | ||
export type Tag = { | ||
name: string | null | ||
attributes: (MdxJsxAttribute | MdxJsxExpressionAttribute)[] | ||
close?: boolean | ||
selfClosing?: boolean | ||
start: Token['start'] | ||
end: Token['start'] | ||
} | ||
/** | ||
* Legacy names: | ||
*/ | ||
export type MDXJsxAttributeValueExpression = MdxJsxAttributeValueExpression | ||
/** | ||
* Legacy names: | ||
*/ | ||
export type MDXJsxAttribute = MdxJsxAttribute | ||
/** | ||
* Legacy names: | ||
*/ | ||
export type MDXJsxExpressionAttribute = MdxJsxExpressionAttribute | ||
/** | ||
* Legacy names: | ||
*/ | ||
export type MDXJsxFlowElement = MdxJsxFlowElement | ||
/** | ||
* Legacy names: | ||
*/ | ||
export type MDXJsxTextElement = MdxJsxTextElement | ||
import('./lib/index.js').MdxJsxExpressionAttribute | ||
export type MdxJsxFlowElement = import('./lib/index.js').MdxJsxFlowElement | ||
export type MdxJsxTextElement = import('./lib/index.js').MdxJsxTextElement | ||
export type ToMarkdownOptions = import('./lib/index.js').ToMarkdownOptions | ||
export {mdxJsxFromMarkdown, mdxJsxToMarkdown} from './lib/index.js' |
471
index.js
/** | ||
* @typedef {import('mdast').Literal} Literal | ||
* @typedef {import('mdast').Parent} Parent | ||
* @typedef {import('mdast-util-from-markdown').Extension} FromMarkdownExtension | ||
* @typedef {import('mdast-util-from-markdown').Handle} FromMarkdownHandle | ||
* @typedef {import('mdast-util-from-markdown').Token} Token | ||
* @typedef {import('mdast-util-to-markdown').Options} ToMarkdownExtension | ||
* @typedef {import('mdast-util-to-markdown').Handle} ToMarkdownHandle | ||
* @typedef {import('mdast-util-to-markdown').Map} ToMarkdownMap | ||
* @typedef {import('mdast-util-from-markdown').OnEnterError} OnEnterError | ||
* @typedef {import('mdast-util-from-markdown').OnExitError} OnExitError | ||
* @typedef {import('estree-jsx').Program} Program | ||
* @typedef {import('./complex-types').MdxJsxAttributeValueExpression} MdxJsxAttributeValueExpression | ||
* @typedef {import('./complex-types').MdxJsxAttribute} MdxJsxAttribute | ||
* @typedef {import('./complex-types').MdxJsxExpressionAttribute} MdxJsxExpressionAttribute | ||
* @typedef {import('./complex-types').MdxJsxFlowElement} MdxJsxFlowElement | ||
* @typedef {import('./complex-types').MdxJsxTextElement} MdxJsxTextElement | ||
* @typedef {{name: string|null, attributes: (MdxJsxAttribute|MdxJsxExpressionAttribute)[], close?: boolean, selfClosing?: boolean, start: Token['start'], end: Token['start']}} Tag | ||
* @typedef {import('./lib/index.js').MdxJsxAttributeValueExpression} MdxJsxAttributeValueExpression | ||
* @typedef {import('./lib/index.js').MdxJsxAttribute} MdxJsxAttribute | ||
* @typedef {import('./lib/index.js').MdxJsxExpressionAttribute} MdxJsxExpressionAttribute | ||
* @typedef {import('./lib/index.js').MdxJsxFlowElement} MdxJsxFlowElement | ||
* @typedef {import('./lib/index.js').MdxJsxTextElement} MdxJsxTextElement | ||
* @typedef {import('./lib/index.js').ToMarkdownOptions} ToMarkdownOptions | ||
*/ | ||
/** | ||
* Legacy names: | ||
* | ||
* @typedef {MdxJsxAttributeValueExpression} MDXJsxAttributeValueExpression | ||
* @typedef {MdxJsxAttribute} MDXJsxAttribute | ||
* @typedef {MdxJsxExpressionAttribute} MDXJsxExpressionAttribute | ||
* @typedef {MdxJsxFlowElement} MDXJsxFlowElement | ||
* @typedef {MdxJsxTextElement} MDXJsxTextElement | ||
*/ | ||
import {parseEntities} from 'parse-entities' | ||
import {stringifyPosition} from 'unist-util-stringify-position' | ||
import {VFileMessage} from 'vfile-message' | ||
import {stringifyEntitiesLight} from 'stringify-entities' | ||
import {containerFlow} from 'mdast-util-to-markdown/lib/util/container-flow.js' | ||
import {containerPhrasing} from 'mdast-util-to-markdown/lib/util/container-phrasing.js' | ||
import {checkQuote} from 'mdast-util-to-markdown/lib/util/check-quote.js' | ||
import {indentLines} from 'mdast-util-to-markdown/lib/util/indent-lines.js' | ||
mdxElement.peek = peekElement | ||
/** @type {FromMarkdownExtension} */ | ||
export const mdxJsxFromMarkdown = { | ||
canContainEols: ['mdxJsxTextElement'], | ||
enter: { | ||
mdxJsxFlowTag: enterMdxJsxTag, | ||
mdxJsxFlowTagClosingMarker: enterMdxJsxTagClosingMarker, | ||
mdxJsxFlowTagAttribute: enterMdxJsxTagAttribute, | ||
mdxJsxFlowTagExpressionAttribute: enterMdxJsxTagExpressionAttribute, | ||
mdxJsxFlowTagAttributeValueLiteral: buffer, | ||
mdxJsxFlowTagAttributeValueExpression: buffer, | ||
mdxJsxFlowTagSelfClosingMarker: enterMdxJsxTagSelfClosingMarker, | ||
mdxJsxTextTag: enterMdxJsxTag, | ||
mdxJsxTextTagClosingMarker: enterMdxJsxTagClosingMarker, | ||
mdxJsxTextTagAttribute: enterMdxJsxTagAttribute, | ||
mdxJsxTextTagExpressionAttribute: enterMdxJsxTagExpressionAttribute, | ||
mdxJsxTextTagAttributeValueLiteral: buffer, | ||
mdxJsxTextTagAttributeValueExpression: buffer, | ||
mdxJsxTextTagSelfClosingMarker: enterMdxJsxTagSelfClosingMarker | ||
}, | ||
exit: { | ||
mdxJsxFlowTagClosingMarker: exitMdxJsxTagClosingMarker, | ||
mdxJsxFlowTagNamePrimary: exitMdxJsxTagNamePrimary, | ||
mdxJsxFlowTagNameMember: exitMdxJsxTagNameMember, | ||
mdxJsxFlowTagNameLocal: exitMdxJsxTagNameLocal, | ||
mdxJsxFlowTagExpressionAttribute: exitMdxJsxTagExpressionAttribute, | ||
mdxJsxFlowTagExpressionAttributeValue: data, | ||
mdxJsxFlowTagAttributeNamePrimary: exitMdxJsxTagAttributeNamePrimary, | ||
mdxJsxFlowTagAttributeNameLocal: exitMdxJsxTagAttributeNameLocal, | ||
mdxJsxFlowTagAttributeValueLiteral: exitMdxJsxTagAttributeValueLiteral, | ||
mdxJsxFlowTagAttributeValueLiteralValue: data, | ||
mdxJsxFlowTagAttributeValueExpression: | ||
exitMdxJsxTagAttributeValueExpression, | ||
mdxJsxFlowTagAttributeValueExpressionValue: data, | ||
mdxJsxFlowTagSelfClosingMarker: exitMdxJsxTagSelfClosingMarker, | ||
mdxJsxFlowTag: exitMdxJsxTag, | ||
mdxJsxTextTagClosingMarker: exitMdxJsxTagClosingMarker, | ||
mdxJsxTextTagNamePrimary: exitMdxJsxTagNamePrimary, | ||
mdxJsxTextTagNameMember: exitMdxJsxTagNameMember, | ||
mdxJsxTextTagNameLocal: exitMdxJsxTagNameLocal, | ||
mdxJsxTextTagExpressionAttribute: exitMdxJsxTagExpressionAttribute, | ||
mdxJsxTextTagExpressionAttributeValue: data, | ||
mdxJsxTextTagAttributeNamePrimary: exitMdxJsxTagAttributeNamePrimary, | ||
mdxJsxTextTagAttributeNameLocal: exitMdxJsxTagAttributeNameLocal, | ||
mdxJsxTextTagAttributeValueLiteral: exitMdxJsxTagAttributeValueLiteral, | ||
mdxJsxTextTagAttributeValueLiteralValue: data, | ||
mdxJsxTextTagAttributeValueExpression: | ||
exitMdxJsxTagAttributeValueExpression, | ||
mdxJsxTextTagAttributeValueExpressionValue: data, | ||
mdxJsxTextTagSelfClosingMarker: exitMdxJsxTagSelfClosingMarker, | ||
mdxJsxTextTag: exitMdxJsxTag | ||
} | ||
} | ||
/** @type {ToMarkdownExtension} */ | ||
export const mdxJsxToMarkdown = { | ||
handlers: { | ||
mdxJsxFlowElement: mdxElement, | ||
mdxJsxTextElement: mdxElement | ||
}, | ||
unsafe: [ | ||
{character: '<', inConstruct: ['phrasing']}, | ||
{atBreak: true, character: '<'} | ||
], | ||
fences: true, | ||
resourceLink: true | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function buffer() { | ||
this.buffer() | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function data(token) { | ||
this.config.enter.data.call(this, token) | ||
this.config.exit.data.call(this, token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTag(token) { | ||
/** @type {Tag} */ | ||
const tag = {name: null, attributes: [], start: token.start, end: token.end} | ||
if (!this.getData('mdxJsxTagStack')) this.setData('mdxJsxTagStack', []) | ||
this.setData('mdxJsxTag', tag) | ||
this.buffer() | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTagClosingMarker(token) { | ||
const stack = /** @type {Tag[]} */ (this.getData('mdxJsxTagStack')) | ||
if (stack.length === 0) { | ||
throw new VFileMessage( | ||
'Unexpected closing slash `/` in tag, expected an open tag first', | ||
{start: token.start, end: token.end}, | ||
'mdast-util-mdx-jsx:unexpected-closing-slash' | ||
) | ||
} | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTagAnyAttribute(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
if (tag.close) { | ||
throw new VFileMessage( | ||
'Unexpected attribute in closing tag, expected the end of the tag', | ||
{start: token.start, end: token.end}, | ||
'mdast-util-mdx-jsx:unexpected-attribute' | ||
) | ||
} | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTagSelfClosingMarker(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
if (tag.close) { | ||
throw new VFileMessage( | ||
'Unexpected self-closing slash `/` in closing tag, expected the end of the tag', | ||
{start: token.start, end: token.end}, | ||
'mdast-util-mdx-jsx:unexpected-self-closing-slash' | ||
) | ||
} | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagClosingMarker() { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.close = true | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagNamePrimary(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.name = this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagNameMember(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.name += '.' + this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagNameLocal(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.name += ':' + this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTagAttribute(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
enterMdxJsxTagAnyAttribute.call(this, token) | ||
tag.attributes.push({type: 'mdxJsxAttribute', name: '', value: null}) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function enterMdxJsxTagExpressionAttribute(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
enterMdxJsxTagAnyAttribute.call(this, token) | ||
tag.attributes.push({type: 'mdxJsxExpressionAttribute', value: ''}) | ||
this.buffer() | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagExpressionAttribute(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const tail = /** @type {MdxJsxExpressionAttribute} */ ( | ||
tag.attributes[tag.attributes.length - 1] | ||
) | ||
/** @type {Program|undefined} */ | ||
// @ts-expect-error: custom. | ||
const estree = token.estree | ||
tail.value = this.resume() | ||
if (estree) { | ||
tail.data = {estree} | ||
} | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagAttributeNamePrimary(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const node = /** @type {MdxJsxAttribute} */ ( | ||
tag.attributes[tag.attributes.length - 1] | ||
) | ||
node.name = this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagAttributeNameLocal(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const node = /** @type {MdxJsxAttribute} */ ( | ||
tag.attributes[tag.attributes.length - 1] | ||
) | ||
node.name += ':' + this.sliceSerialize(token) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagAttributeValueLiteral() { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.attributes[tag.attributes.length - 1].value = parseEntities( | ||
this.resume(), | ||
{nonTerminated: false} | ||
) | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagAttributeValueExpression(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const tail = /** @type {MdxJsxAttribute} */ ( | ||
tag.attributes[tag.attributes.length - 1] | ||
) | ||
/** @type {MdxJsxAttributeValueExpression} */ | ||
const node = {type: 'mdxJsxAttributeValueExpression', value: this.resume()} | ||
/** @type {Program|undefined} */ | ||
// @ts-expect-error: custom. | ||
const estree = token.estree | ||
if (estree) { | ||
node.data = {estree} | ||
} | ||
tail.value = node | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTagSelfClosingMarker() { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
tag.selfClosing = true | ||
} | ||
/** @type {FromMarkdownHandle} */ | ||
function exitMdxJsxTag(token) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const stack = /** @type {Tag[]} */ (this.getData('mdxJsxTagStack')) | ||
const tail = stack[stack.length - 1] | ||
if (tag.close && tail.name !== tag.name) { | ||
throw new VFileMessage( | ||
'Unexpected closing tag `' + | ||
serializeAbbreviatedTag(tag) + | ||
'`, expected corresponding closing tag for `' + | ||
serializeAbbreviatedTag(tail) + | ||
'` (' + | ||
stringifyPosition(tail) + | ||
')', | ||
{start: token.start, end: token.end}, | ||
'mdast-util-mdx-jsx:end-tag-mismatch' | ||
) | ||
} | ||
// End of a tag, so drop the buffer. | ||
this.resume() | ||
if (tag.close) { | ||
stack.pop() | ||
} else { | ||
this.enter( | ||
{ | ||
type: | ||
token.type === 'mdxJsxTextTag' | ||
? 'mdxJsxTextElement' | ||
: 'mdxJsxFlowElement', | ||
name: tag.name, | ||
attributes: tag.attributes, | ||
children: [] | ||
}, | ||
token, | ||
onErrorRightIsTag | ||
) | ||
} | ||
if (tag.selfClosing || tag.close) { | ||
this.exit(token, onErrorLeftIsTag) | ||
} else { | ||
stack.push(tag) | ||
} | ||
} | ||
/** @type {OnEnterError} */ | ||
function onErrorRightIsTag(closing, open) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
const place = closing ? ' before the end of `' + closing.type + '`' : '' | ||
const position = closing | ||
? {start: closing.start, end: closing.end} | ||
: undefined | ||
throw new VFileMessage( | ||
'Expected a closing tag for `' + | ||
serializeAbbreviatedTag(tag) + | ||
'` (' + | ||
stringifyPosition({start: open.start, end: open.end}) + | ||
')' + | ||
place, | ||
position, | ||
'mdast-util-mdx-jsx:end-tag-mismatch' | ||
) | ||
} | ||
/** @type {OnExitError} */ | ||
function onErrorLeftIsTag(a, b) { | ||
const tag = /** @type {Tag} */ (this.getData('mdxJsxTag')) | ||
throw new VFileMessage( | ||
'Expected the closing tag `' + | ||
serializeAbbreviatedTag(tag) + | ||
'` either after the end of `' + | ||
b.type + | ||
'` (' + | ||
stringifyPosition(b.end) + | ||
') or another opening tag after the start of `' + | ||
b.type + | ||
'` (' + | ||
stringifyPosition(b.start) + | ||
')', | ||
{start: a.start, end: a.end}, | ||
'mdast-util-mdx-jsx:end-tag-mismatch' | ||
) | ||
} | ||
/** | ||
* Serialize a tag, excluding attributes. | ||
* `self-closing` is not supported, because we don’t need it yet. | ||
* | ||
* @param {Tag} tag | ||
* @returns {string} | ||
*/ | ||
function serializeAbbreviatedTag(tag) { | ||
return '<' + (tag.close ? '/' : '') + (tag.name || '') + '>' | ||
} | ||
/** | ||
* @type {ToMarkdownHandle} | ||
* @param {MdxJsxFlowElement|MdxJsxTextElement} node | ||
*/ | ||
// eslint-disable-next-line complexity | ||
function mdxElement(node, _, context) { | ||
const selfClosing = | ||
node.name && (!node.children || node.children.length === 0) | ||
const quote = checkQuote(context) | ||
const exit = context.enter(node.type) | ||
let attributeValue = '' | ||
let index = -1 | ||
/** @type {Array<string>} */ | ||
const attributes = [] | ||
/** @type {string} */ | ||
let result | ||
// None. | ||
if (node.attributes && node.attributes.length > 0) { | ||
if (!node.name) { | ||
throw new Error('Cannot serialize fragment w/ attributes') | ||
} | ||
const isMultiFlow = | ||
node.type === 'mdxJsxFlowElement' && node.attributes.length > 1 | ||
while (++index < node.attributes.length) { | ||
const attribute = node.attributes[index] | ||
if (attribute.type === 'mdxJsxExpressionAttribute') { | ||
result = '{' + (attribute.value || '') + '}' | ||
} else { | ||
if (!attribute.name) { | ||
throw new Error('Cannot serialize attribute w/o name') | ||
} | ||
result = | ||
attribute.name + | ||
(attribute.value === undefined || attribute.value === null | ||
? '' | ||
: '=' + | ||
(typeof attribute.value === 'object' | ||
? '{' + (attribute.value.value || '') + '}' | ||
: quote + | ||
stringifyEntitiesLight(attribute.value, {subset: [quote]}) + | ||
quote)) | ||
} | ||
attributes.push((isMultiFlow ? '\n ' : ' ') + result) | ||
} | ||
attributeValue = attributes.join('') + (isMultiFlow ? '\n' : '') | ||
} | ||
const value = | ||
'<' + | ||
(node.name || '') + | ||
attributeValue + | ||
(selfClosing ? '/' : '') + | ||
'>' + | ||
(node.children && node.children.length > 0 | ||
? node.type === 'mdxJsxFlowElement' | ||
? '\n' + indent(containerFlow(node, context)) + '\n' | ||
: containerPhrasing(node, context, {before: '<', after: '>'}) | ||
: '') + | ||
(selfClosing ? '' : '</' + (node.name || '') + '>') | ||
exit() | ||
return value | ||
} | ||
/** | ||
* @type {ToMarkdownHandle} | ||
*/ | ||
function peekElement() { | ||
return '<' | ||
} | ||
/** | ||
* @param {string} value | ||
* @returns {string} | ||
*/ | ||
function indent(value) { | ||
return indentLines(value, map) | ||
/** @type {ToMarkdownMap} */ | ||
function map(line, _, blank) { | ||
return (blank ? '' : ' ') + line | ||
} | ||
} | ||
export {mdxJsxFromMarkdown, mdxJsxToMarkdown} from './lib/index.js' |
{ | ||
"name": "mdast-util-mdx-jsx", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "mdast extension to parse and serialize MDX or MDX.js JSX", | ||
@@ -34,3 +34,3 @@ "license": "MIT", | ||
"files": [ | ||
"complex-types.d.ts", | ||
"lib/", | ||
"index.d.ts", | ||
@@ -41,4 +41,6 @@ "index.js" | ||
"@types/estree-jsx": "^0.0.1", | ||
"@types/hast": "^2.0.0", | ||
"@types/mdast": "^3.0.0", | ||
"mdast-util-to-markdown": "^1.0.0", | ||
"ccount": "^2.0.0", | ||
"mdast-util-to-markdown": "^1.3.0", | ||
"parse-entities": "^4.0.0", | ||
@@ -63,6 +65,6 @@ "stringify-entities": "^4.0.0", | ||
"typescript": "^4.0.0", | ||
"xo": "^0.46.0" | ||
"xo": "^0.47.0" | ||
}, | ||
"scripts": { | ||
"build": "rimraf \"{index,test}.d.ts\" && tsc && type-coverage", | ||
"build": "rimraf \"lib/index.d.ts\" \"*.d.ts\" && tsc && type-coverage", | ||
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", | ||
@@ -69,0 +71,0 @@ "test-api": "node --conditions development test.js", |
263
readme.md
@@ -11,22 +11,49 @@ # mdast-util-mdx-jsx | ||
Extension for [`mdast-util-from-markdown`][from-markdown] and/or | ||
[`mdast-util-to-markdown`][to-markdown] to support MDX (or MDX.js) JSX. | ||
When parsing (`from-markdown`), must be combined with | ||
[`micromark-extension-mdx-jsx`][extension]. | ||
Extensions to parse and serialize JSX between mdast and markdown. | ||
This utility handles parsing and serializing. | ||
See [`micromark-extension-mdx-jsx`][extension] for how the syntax works. | ||
## Contents | ||
* [What is this?](#what-is-this) | ||
* [When to use this](#when-to-use-this) | ||
* [Install](#install) | ||
* [Use](#use) | ||
* [API](#api) | ||
* [`mdxJsxFromMarkdown()`](#mdxjsxfrommarkdown) | ||
* [`mdxJsxToMarkdown(options?)`](#mdxjsxtomarkdownoptions) | ||
* [Syntax tree](#syntax-tree) | ||
* [Nodes](#nodes) | ||
* [Mixin](#mixin) | ||
* [Content model](#content-model) | ||
* [Types](#types) | ||
* [Compatibility](#compatibility) | ||
* [Related](#related) | ||
* [Contribute](#contribute) | ||
* [License](#license) | ||
## What is this? | ||
This package contains extensions that add support for the JSX syntax enabled by | ||
MDX to [`mdast-util-from-markdown`][mdast-util-from-markdown] and | ||
[`mdast-util-to-markdown`][mdast-util-to-markdown]. | ||
[JSX][] is an XML-like syntax extension to ECMAScript (JavaScript), which MDX | ||
brings to markdown. | ||
For more info on MDX, see [What is MDX?][what-is-mdx] | ||
## When to use this | ||
Use [`mdast-util-mdx`][mdast-util-mdx] if you want all of MDX / MDX.js. | ||
Use this otherwise. | ||
These tools are all rather low-level. | ||
In most cases, you’d want to use [`remark-mdx`][remark-mdx] with remark instead. | ||
When you are working with syntax trees and want all of MDX, use | ||
[`mdast-util-mdx`][mdast-util-mdx] instead. | ||
When working with `mdast-util-from-markdown`, you’d want to combine this package | ||
with [`micromark-extension-mdx-jsx`][micromark-extension-mdx-jsx]. | ||
## 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 12.20+, 14.14+, or 16.0+), install with [npm][]: | ||
[npm][]: | ||
```sh | ||
@@ -36,5 +63,19 @@ npm install mdast-util-mdx-jsx | ||
In Deno with [Skypack][]: | ||
```js | ||
import {mdxJsxFromMarkdown, mdxJsxToMarkdown} from 'https://cdn.skypack.dev/mdast-util-mdx-jsx@1?dts' | ||
``` | ||
In browsers with [Skypack][]: | ||
```html | ||
<script type="module"> | ||
import {mdxJsxFromMarkdown, mdxJsxToMarkdown} from 'https://cdn.skypack.dev/mdast-util-mdx-jsx@1?min' | ||
</script> | ||
``` | ||
## Use | ||
Say we have an MDX.js file, `example.mdx`: | ||
Say our document `example.mdx` contains: | ||
@@ -51,3 +92,3 @@ ```mdx | ||
And our module, `example.js`, looks as follows: | ||
…and our module `example.js` looks as follows: | ||
@@ -66,3 +107,3 @@ ```js | ||
extensions: [mdxJsx({acorn: acorn, addResult: true})], | ||
mdastExtensions: [mdxJsxFromMarkdown] | ||
mdastExtensions: [mdxJsxFromMarkdown()] | ||
}) | ||
@@ -72,3 +113,3 @@ | ||
const out = toMarkdown(tree, {extensions: [mdxJsxToMarkdown]}) | ||
const out = toMarkdown(tree, {extensions: [mdxJsxToMarkdown()]}) | ||
@@ -78,3 +119,3 @@ console.log(out) | ||
Now, running `node example` yields (positional info removed for brevity): | ||
…now running `node example.js` yields (positional info removed for brevity): | ||
@@ -166,3 +207,3 @@ ```js | ||
<MyComponent {...props}/> | ||
<MyComponent {...props} /> | ||
@@ -174,20 +215,50 @@ <abbr title="Hypertext Markup Language">HTML</abbr> is a lovely language. | ||
### `mdxJsxFromMarkdown` | ||
This package exports the following identifiers: `mdxJsxFromMarkdown`, | ||
`mdxJsxToMarkdown`. | ||
There is no default export. | ||
### `mdxJsxToMarkdown` | ||
### `mdxJsxFromMarkdown()` | ||
Support MDX (or MDX.js) JSX. | ||
The exports are extensions, respectively for | ||
[`mdast-util-from-markdown`][from-markdown] and | ||
[`mdast-util-to-markdown`][to-markdown]. | ||
Function that can be called to get an extension for | ||
[`mdast-util-from-markdown`][mdast-util-from-markdown]. | ||
When using the [syntax extension][extension] with `addResult`, nodes will have a | ||
`data.estree` field set to an [ESTree][]. | ||
When using [`micromark-extension-mdx-jsx`][micromark-extension-mdx-jsx] | ||
with `options.addResult`, nodes will have a `data.estree` field set to an | ||
[ESTree][]. | ||
There are no options, but passing [`options.quote`][quote] to | ||
`mdast-util-to-markdown` is honored for attributes. | ||
### `mdxJsxToMarkdown(options?)` | ||
this extension configures [`mdast-util-to-markdown`][to-markdown] with | ||
`fences: true` and `resourceLink: true` too, do not overwrite them! | ||
Function that can be called to get an extension for | ||
[`mdast-util-to-markdown`][mdast-util-to-markdown]. | ||
This extension configures `mdast-util-to-markdown` with | ||
[`options.fences: true`][mdast-util-to-markdown-fences] and | ||
[`options.resourceLink: true`][mdast-util-to-markdown-resourcelink] too, do not | ||
overwrite them! | ||
##### `options` | ||
Configuration (optional). | ||
###### `options.quote` | ||
Preferred quote to use around attribute values (`'"'` or `"'"`, default: `'"'`). | ||
###### `options.quoteSmart` | ||
Use the other quote if that results in less bytes (`boolean`, default: `false`). | ||
###### `options.tightSelfClosing` | ||
Do not use an extra space when closing self-closing elements: `<img/>` instead | ||
of `<img />` (`boolean`, default: `false`). | ||
###### `options.printWidth` | ||
Try and wrap syntax as this width (`number`, default: `Infinity`). | ||
When set to a finite number (say, `80`), the formatter will print attributes on | ||
separate lines when a tag doesn’t fit on one line. | ||
The normal behavior is to print attributes with spaces between them instead of | ||
line endings. | ||
## Syntax tree | ||
@@ -199,15 +270,15 @@ | ||
#### `MDXJsxFlowElement` | ||
###### `MdxJsxFlowElement` | ||
```idl | ||
interface MDXJsxFlowElement <: Parent { | ||
interface MdxJsxFlowElement <: Parent { | ||
type: "mdxJsxFlowElement" | ||
} | ||
MDXJsxFlowElement includes MDXJsxElement | ||
MdxJsxFlowElement includes MdxJsxElement | ||
``` | ||
**MDXJsxFlowElement** (**[Parent][dfn-parent]**) represents JSX in flow (block). | ||
**MdxJsxFlowElement** (**[Parent][dfn-parent]**) represents JSX in flow (block). | ||
It can be used where **[flow][dfn-content-flow]** content is expected. | ||
It includes the mixin **[MDXJsxElement][dfn-mixin-mdx-jsx-element]**. | ||
It includes the mixin **[MdxJsxElement][dfn-mixin-mdx-jsx-element]**. | ||
@@ -233,17 +304,17 @@ For example, the following markdown: | ||
#### `MDXJsxTextElement` | ||
###### `MdxJsxTextElement` | ||
```idl | ||
interface MDXJsxTextElement <: Parent { | ||
interface MdxJsxTextElement <: Parent { | ||
type: "mdxJsxTextElement" | ||
} | ||
MDXJsxTextElement includes MDXJsxElement | ||
MdxJsxTextElement includes MdxJsxElement | ||
``` | ||
**MDXJsxTextElement** (**[Parent][dfn-parent]**) represents JSX in text (span, | ||
**MdxJsxTextElement** (**[Parent][dfn-parent]**) represents JSX in text (span, | ||
inline). | ||
It can be used where **[phrasing][dfn-content-phrasing]** content is | ||
expected. | ||
It includes the mixin **[MDXJsxElement][dfn-mixin-mdx-jsx-element]**. | ||
It includes the mixin **[MdxJsxElement][dfn-mixin-mdx-jsx-element]**. | ||
@@ -269,21 +340,21 @@ For example, the following markdown: | ||
### `MDXJsxElement` | ||
###### `MdxJsxElement` | ||
```idl | ||
interface mixin MDXJsxElement { | ||
interface mixin MdxJsxElement { | ||
name: string? | ||
attributes: [MDXJsxExpressionAttribute | MDXJsxAttribute] | ||
attributes: [MdxJsxExpressionAttribute | MdxJsxAttribute] | ||
} | ||
interface MDXJsxExpressionAttribute <: Literal { | ||
interface MdxJsxExpressionAttribute <: Literal { | ||
type: "mdxJsxExpressionAttribute" | ||
} | ||
interface MDXJsxAttribute <: Node { | ||
interface MdxJsxAttribute <: Node { | ||
type: "mdxJsxAttribute" | ||
name: string | ||
value: MDXJsxAttributeValueExpression | string? | ||
value: MdxJsxAttributeValueExpression | string? | ||
} | ||
interface MDXJsxAttributeValueExpression <: Literal { | ||
interface MdxJsxAttributeValueExpression <: Literal { | ||
type: "mdxJsxAttributeValueExpression" | ||
@@ -293,3 +364,3 @@ } | ||
**MDXJsxElement** represents a JSX element. | ||
**MdxJsxElement** represents a JSX element. | ||
@@ -301,9 +372,9 @@ The `name` field can be present and represents an identifier. | ||
The `attributes` field represents information associated with the node. | ||
The value of the `attributes` field is a list of **MDXJsxExpressionAttribute** | ||
and **MDXJsxAttribute** nodes. | ||
The value of the `attributes` field is a list of **MdxJsxExpressionAttribute** | ||
and **MdxJsxAttribute** nodes. | ||
**MDXJsxExpressionAttribute** represents an expression (typically in a | ||
**MdxJsxExpressionAttribute** represents an expression (typically in a | ||
programming language) that when evaluated results in multiple attributes. | ||
**MDXJsxAttribute** represents a single attribute. | ||
**MdxJsxAttribute** represents a single attribute. | ||
The `name` field must be present. | ||
@@ -316,30 +387,58 @@ The `value` field can be present, in which case it is either a string (a static | ||
#### `FlowContent` (MDX JSX) | ||
###### `FlowContent` (MDX JSX) | ||
```idl | ||
type MDXJsxFlowContent = MDXJsxFlowElement | FlowContent | ||
type MdxJsxFlowContent = MdxJsxFlowElement | FlowContent | ||
``` | ||
#### `PhrasingContent` (MDX JSX) | ||
###### `PhrasingContent` (MDX JSX) | ||
```idl | ||
type MDXJsxPhrasingContent = MDXJsxTextElement | PhrasingContent | ||
type MdxJsxPhrasingContent = MdxJsxTextElement | PhrasingContent | ||
``` | ||
## Types | ||
This package is fully typed with [TypeScript][]. | ||
It exports the `MdxJsxAttributeValueExpression`, `MdxJsxAttribute`, | ||
`MdxJsxExpressionAttribute`, `MdxJsxFlowElement`, and `MdxJsxTextElement` types | ||
that represents the supported nodes. | ||
It also exports `ToMarkdownOptions`, which represents the structure of the | ||
respective options. | ||
It also registers the node types with `@types/mdast`. | ||
If you’re working with the syntax tree, make sure to import this plugin | ||
somewhere in your types, as that registers the new node types in the tree. | ||
```js | ||
/** @typedef {import('mdast-util-mdx-jsx')} */ | ||
import {visit} from 'unist-util-visit' | ||
/** @type {import('mdast').Root} */ | ||
const tree = getMdastNodeSomeHow() | ||
visit(tree, (node) => { | ||
// `node` can now be one of the JSX nodes. | ||
}) | ||
``` | ||
## Compatibility | ||
Projects maintained by the unified collective are compatible with all maintained | ||
versions of Node.js. | ||
As of now, that is Node.js 12.20+, 14.14+, and 16.0+. | ||
Our projects sometimes work with older versions, but this is not guaranteed. | ||
This plugin works with `mdast-util-from-markdown` version 1+ and | ||
`mdast-util-to-markdown` version 1+. | ||
## Related | ||
* [`remarkjs/remark`][remark] | ||
— markdown processor powered by plugins | ||
* [`micromark/micromark-extension-mdx-jsx`][micromark-extension-mdx-jsx] | ||
— support MDX JSX in micromark | ||
* [`syntax-tree/mdast-util-mdx`][mdast-util-mdx] | ||
— support MDX in mdast | ||
* [`remarkjs/remark-mdx`][remark-mdx] | ||
— remark plugin to support MDX | ||
* [`syntax-tree/mdast-util-from-markdown`][from-markdown] | ||
— mdast parser using `micromark` to create mdast from markdown | ||
* [`syntax-tree/mdast-util-to-markdown`][to-markdown] | ||
— mdast serializer to create markdown from mdast | ||
* [`syntax-tree/mdast-util-mdx`][mdast-util-mdx] | ||
— mdast utility to support all of MDX | ||
* [`micromark/micromark`][micromark] | ||
— the smallest commonmark-compliant markdown parser that exists | ||
* [`micromark/micromark-extension-mdx-jsx`][extension] | ||
— micromark extension to parse JSX | ||
— support MDX in remark | ||
@@ -388,2 +487,4 @@ ## Contribute | ||
[skypack]: https://www.skypack.dev | ||
[license]: license | ||
@@ -399,18 +500,14 @@ | ||
[mdast]: https://github.com/syntax-tree/mdast | ||
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c | ||
[remark]: https://github.com/remarkjs/remark | ||
[typescript]: https://www.typescriptlang.org | ||
[from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown | ||
[mdast]: https://github.com/syntax-tree/mdast | ||
[to-markdown]: https://github.com/syntax-tree/mdast-util-to-markdown | ||
[mdast-util-from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown | ||
[micromark]: https://github.com/micromark/micromark | ||
[mdast-util-to-markdown]: https://github.com/syntax-tree/mdast-util-to-markdown | ||
[extension]: https://github.com/micromark/micromark-extension-mdxjs-esm | ||
[mdast-util-mdx]: https://github.com/syntax-tree/mdast-util-mdx | ||
[quote]: https://github.com/syntax-tree/mdast-util-to-markdown#optionsquote | ||
[estree]: https://github.com/estree/estree | ||
@@ -427,1 +524,11 @@ | ||
[remark-mdx]: https://github.com/mdx-js/mdx/tree/main/packages/remark-mdx | ||
[jsx]: https://facebook.github.io/jsx/ | ||
[what-is-mdx]: https://mdxjs.com/docs/what-is-mdx/ | ||
[micromark-extension-mdx-jsx]: https://github.com/micromark/micromark-extension-mdx-jsx | ||
[mdast-util-to-markdown-fences]: https://github.com/syntax-tree/mdast-util-to-markdown#optionsfences | ||
[mdast-util-to-markdown-resourcelink]: https://github.com/syntax-tree/mdast-util-to-markdown#optionsresourcelink |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
39933
8
609
518
10
1
+ Added@types/hast@^2.0.0
+ Addedccount@^2.0.0
+ Added@types/hast@2.3.10(transitive)
+ Addedccount@2.0.1(transitive)