diff --git a/changelog_unreleased/mdx/pr-6949.md b/changelog_unreleased/mdx/pr-6949.md new file mode 100644 index 000000000..bf24baca1 --- /dev/null +++ b/changelog_unreleased/mdx/pr-6949.md @@ -0,0 +1,43 @@ +#### Fix MDX html parsing errors ([#6949](https://github.com/prettier/prettier/pull/6949) by [@Tigge](https://github.com/Tigge) & [@thorn0](https://github.com/thorn0)) + +MDX parsing for JSX failed when encountering JSX elements that where not +parsable as HTML. Such as `test` + + +````md + + +test + + +
+ hi + { hello } + { /* another comment */} +
+
+ + + +``` +SyntaxError: Unexpected closing tag "Tag". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (1:35) +> 1 | test + | ^ + 2 | + 3 | + 4 |
+``` + + + +test + + +
+ hi + {hello} + {/* another comment */} +
+
+ +```` diff --git a/src/language-js/preprocess.js b/src/language-js/preprocess.js index 57489c7b7..fc39070b8 100644 --- a/src/language-js/preprocess.js +++ b/src/language-js/preprocess.js @@ -10,7 +10,8 @@ function preprocess(ast, options) { return Object.assign({}, ast, { type: options.parser.startsWith("__") ? "JsExpressionRoot" : "JsonRoot", node: ast, - comments: [] + comments: [], + rootMarker: options.rootMarker }); default: return ast; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index c9f73d0ca..df2dea511 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -5398,8 +5398,12 @@ function printJSXElement(path, options, print) { containsMultipleAttributes || containsMultipleExpressions; + const isMdxBlock = path.getParentNode().rootMarker === "mdx"; + const rawJsxWhitespace = options.singleQuote ? "{' '}" : '{" "}'; - const jsxWhitespace = ifBreak(concat([rawJsxWhitespace, softline]), " "); + const jsxWhitespace = isMdxBlock + ? concat([" "]) + : ifBreak(concat([rawJsxWhitespace, softline]), " "); const isFacebookTranslationTag = n.openingElement && @@ -5519,6 +5523,10 @@ function printJSXElement(path, options, print) { ? fill(multilineChildren) : group(concat(multilineChildren), { shouldBreak: true }); + if (isMdxBlock) { + return content; + } + const multiLineElem = group( concat([ openingLines, diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js index bd5339131..d06de68f6 100644 --- a/src/language-markdown/embed.js +++ b/src/language-markdown/embed.js @@ -57,7 +57,10 @@ function embed(path, print, textToDoc, options) { case "importExport": return textToDoc(node.value, { parser: "babel" }); case "jsx": - return textToDoc(node.value, { parser: "__js_expression" }); + return textToDoc(`<$>${node.value}`, { + parser: "__js_expression", + rootMarker: "mdx" + }); } return null; diff --git a/src/language-markdown/parser-markdown.js b/src/language-markdown/parser-markdown.js index 30b0b41b0..5a33dc8a2 100644 --- a/src/language-markdown/parser-markdown.js +++ b/src/language-markdown/parser-markdown.js @@ -7,7 +7,6 @@ const parseFrontMatter = require("../utils/front-matter"); const { mapAst, INLINE_NODE_WRAPPER_TYPES } = require("./utils"); const mdx = require("./mdx"); const remarkMath = require("remark-math"); -const htmlParser = require("../language-html/parser-html").parsers.html; /** * based on [MDAST](https://github.com/syntax-tree/mdast) with following modifications: @@ -60,29 +59,7 @@ function htmlToJsx() { return node; } - const nodes = htmlParser.parse(node.value).children; - - // find out if there are adjacent JSX elements which should be allowed in mdx alike in markdown - if (nodes.length <= 1) { - return Object.assign({}, node, { type: "jsx" }); - } - - return nodes.reduce((newNodes, { sourceSpan: position, type }) => { - const value = node.value.slice( - position.start.offset, - position.end.offset - ); - - if (value) { - newNodes.push({ - type: type === "element" ? "jsx" : type, - value, - position - }); - } - - return newNodes; - }, []); + return Object.assign({}, node, { type: "jsx" }); }); } diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index b4effa98f..dc588f1ce 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -35,12 +35,7 @@ const { replaceEndOfLineWith } = require("../common/util"); const TRAILING_HARDLINE_NODES = ["importExport"]; const SINGLE_LINE_NODE_TYPES = ["heading", "tableCell", "link"]; -const SIBLING_NODE_TYPES = [ - "listItem", - "definition", - "footnoteDefinition", - "jsx" -]; +const SIBLING_NODE_TYPES = ["listItem", "definition", "footnoteDefinition"]; function genericPrint(path, options, print) { const node = path.getValue(); @@ -769,55 +764,35 @@ function isPrettierIgnore(node) { return match === null ? false : match[1] ? match[1] : "next"; } -function isInlineNode(node) { - return node && INLINE_NODE_TYPES.indexOf(node.type) !== -1; -} - -function isEndsWithHardLine(node) { - return node && /\n+$/.test(node.value); -} - -function last(nodes) { - return nodes && nodes[nodes.length - 1]; -} - -function shouldNotPrePrintHardline(node, { parentNode, parts, prevNode }) { - const isFirstNode = parts.length === 0; +function shouldNotPrePrintHardline(node, data) { + const isFirstNode = data.parts.length === 0; + const isInlineNode = INLINE_NODE_TYPES.indexOf(node.type) !== -1; const isInlineHTML = node.type === "html" && - INLINE_NODE_WRAPPER_TYPES.indexOf(parentNode.type) !== -1; + INLINE_NODE_WRAPPER_TYPES.indexOf(data.parentNode.type) !== -1; - const isAfterHardlineNode = - prevNode && - (isEndsWithHardLine(prevNode) || - isEndsWithHardLine(last(prevNode.children))); - - return ( - isFirstNode || isInlineNode(node) || isInlineHTML || isAfterHardlineNode - ); + return isFirstNode || isInlineNode || isInlineHTML; } -function shouldPrePrintDoubleHardline(node, { parentNode, prevNode }) { - const prevNodeType = prevNode && prevNode.type; - const nodeType = node.type; - - const isSequence = prevNodeType === nodeType; +function shouldPrePrintDoubleHardline(node, data) { + const isSequence = (data.prevNode && data.prevNode.type) === node.type; const isSiblingNode = - isSequence && SIBLING_NODE_TYPES.indexOf(nodeType) !== -1; + isSequence && SIBLING_NODE_TYPES.indexOf(node.type) !== -1; - const isInTightListItem = parentNode.type === "listItem" && !parentNode.loose; - const isPrevNodeLooseListItem = prevNodeType === "listItem" && prevNode.loose; - const isPrevNodePrettierIgnore = isPrettierIgnore(prevNode) === "next"; + const isInTightListItem = + data.parentNode.type === "listItem" && !data.parentNode.loose; + + const isPrevNodeLooseListItem = + data.prevNode && data.prevNode.type === "listItem" && data.prevNode.loose; + + const isPrevNodePrettierIgnore = isPrettierIgnore(data.prevNode) === "next"; const isBlockHtmlWithoutBlankLineBetweenPrevHtml = - nodeType === "html" && - prevNodeType === "html" && - prevNode.position.end.line + 1 === node.position.start.line; - - const isJsxInlineSibling = - (prevNodeType === "jsx" && isInlineNode(node)) || - (nodeType === "jsx" && isInlineNode(prevNode)); + node.type === "html" && + data.prevNode && + data.prevNode.type === "html" && + data.prevNode.position.end.line + 1 === node.position.start.line; return ( isPrevNodeLooseListItem || @@ -825,8 +800,7 @@ function shouldPrePrintDoubleHardline(node, { parentNode, prevNode }) { isSiblingNode || isInTightListItem || isPrevNodePrettierIgnore || - isBlockHtmlWithoutBlankLineBetweenPrevHtml || - isJsxInlineSibling + isBlockHtmlWithoutBlankLineBetweenPrevHtml ) ); } diff --git a/src/language-markdown/utils.js b/src/language-markdown/utils.js index 9fd68f037..dd1f00528 100644 --- a/src/language-markdown/utils.js +++ b/src/language-markdown/utils.js @@ -230,21 +230,11 @@ function mapAst(ast, handler) { return (function preorder(node, index, parentStack) { parentStack = parentStack || []; - let newNode = handler(node, index, parentStack); - if (Array.isArray(newNode)) { - return newNode; - } - - newNode = Object.assign({}, newNode); + const newNode = Object.assign({}, handler(node, index, parentStack)); if (newNode.children) { - newNode.children = newNode.children.reduce((nodes, child, index) => { - let newNodes = preorder(child, index, [newNode].concat(parentStack)); - if (!Array.isArray(newNodes)) { - newNodes = [newNodes]; - } - nodes.push.apply(nodes, newNodes); - return nodes; - }, []); + newNode.children = newNode.children.map((child, index) => { + return preorder(child, index, [newNode].concat(parentStack)); + }); } return newNode; diff --git a/tests/mdx/__snapshots__/jsfmt.spec.js.snap b/tests/mdx/__snapshots__/jsfmt.spec.js.snap index 942f1e22a..f5dc83bc8 100644 --- a/tests/mdx/__snapshots__/jsfmt.spec.js.snap +++ b/tests/mdx/__snapshots__/jsfmt.spec.js.snap @@ -276,7 +276,8 @@ printWidth: 80 test test -123 + +123 --- @@ -352,7 +353,8 @@ semi: false test test -123 + +123 --- @@ -438,6 +440,117 @@ const a = 1 ================================================================================ `; +exports[`levels.mdx 1`] = ` +====================================options===================================== +parsers: ["mdx"] +printWidth: 80 + | printWidth +=====================================input====================================== +import { Foo, Bar } from './Fixture' + +# Hello, world! + + +
+ hi + { hello } + { /* another comment */} +
+
+ +asdfsdf asdfasdf + + +test + +
    +
  • item {' '}
  • +
  • +
+ + +=====================================output===================================== +import { Foo, Bar } from "./Fixture"; + +# Hello, world! + + +
+ hi + {hello} + {/* another comment */} +
+
+ +asdfsdf asdfasdf + + +test + +
    +
  • item
  • +
  • +
+ +================================================================================ +`; + +exports[`levels.mdx 2`] = ` +====================================options===================================== +parsers: ["mdx"] +printWidth: 80 +semi: false + | printWidth +=====================================input====================================== +import { Foo, Bar } from './Fixture' + +# Hello, world! + + +
+ hi + { hello } + { /* another comment */} +
+
+ +asdfsdf asdfasdf + + +test + +
    +
  • item {' '}
  • +
  • +
+ + +=====================================output===================================== +import { Foo, Bar } from "./Fixture" + +# Hello, world! + + +
+ hi + {hello} + {/* another comment */} +
+
+ +asdfsdf asdfasdf + + +test + +
    +
  • item
  • +
  • +
+ +================================================================================ +`; + exports[`mixed.mdx 1`] = ` ====================================options===================================== parsers: ["mdx"] diff --git a/tests/mdx/levels.mdx b/tests/mdx/levels.mdx new file mode 100644 index 000000000..b036a001d --- /dev/null +++ b/tests/mdx/levels.mdx @@ -0,0 +1,22 @@ +import { Foo, Bar } from './Fixture' + +# Hello, world! + + +
+ hi + { hello } + { /* another comment */} +
+
+ +asdfsdf asdfasdf + + +test + +
    +
  • item {' '}
  • +
  • +
+