diff --git a/changelog_unreleased/mdx/17996.md b/changelog_unreleased/mdx/17996.md
new file mode 100644
index 000000000..371c82c87
--- /dev/null
+++ b/changelog_unreleased/mdx/17996.md
@@ -0,0 +1,18 @@
+#### Fix `import` and `export` parsing (#17996 by @kovsu & @fisker)
+
+
+```mdx
+{/* Input */}
+- import is a word in lists
+- export is a word in lists, too!
+
+{/* Prettier stable */}
+- import is a word in list
+ s
+- export is a word in lists, too
+ !
+
+{/* Prettier main */}
+- import is a word in lists
+- export is a word in lists, too!
+```
diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js
index 5108091f4..c017487a4 100644
--- a/src/language-markdown/embed.js
+++ b/src/language-markdown/embed.js
@@ -57,7 +57,12 @@ function embed(path, options) {
// MDX
case "import":
case "export":
- return (textToDoc) => textToDoc(node.value, { parser: "babel" });
+ return (textToDoc) =>
+ textToDoc(node.value, {
+ // TODO: Rename this option since it's not used in HTML
+ __onHtmlBindingRoot: (ast) => validateImportExport(ast, node.type),
+ parser: "babel",
+ });
case "jsx":
return (textToDoc) =>
textToDoc(`<$>${node.value}$>`, {
@@ -69,4 +74,22 @@ function embed(path, options) {
return null;
}
+function validateImportExport(ast, type) {
+ const {
+ program: { body },
+ } = ast;
+
+ // https://github.com/mdx-js/mdx/blob/3430138958c9c0344ecad9d59e0d6b5d72bedae3/packages/remark-mdx/extract-imports-and-exports.js#L16
+ if (
+ !body.every(
+ (node) =>
+ node.type === "ImportDeclaration" ||
+ node.type === "ExportDefaultDeclaration" ||
+ node.type === "ExportNamedDeclaration",
+ )
+ ) {
+ throw new Error(`Unexpected '${type}' in MDX.`);
+ }
+}
+
export default embed;
diff --git a/src/language-markdown/mdx.js b/src/language-markdown/mdx.js
index 3dd36a77a..f5d1aa918 100644
--- a/src/language-markdown/mdx.js
+++ b/src/language-markdown/mdx.js
@@ -1,5 +1,5 @@
/**
- * modified from https://github.com/mdx-js/mdx/blob/master/packages/mdx
+ * modified from https://github.com/mdx-js/mdx/blob/c91b00c673bcf3e7c28b861fd692b69016026c45/packages/remark-mdx/index.js
*
* The MIT License (MIT)
*
@@ -31,12 +31,13 @@ const EMPTY_NEWLINE = "\n\n";
const isImport = (text) => IMPORT_REGEX.test(text);
const isExport = (text) => EXPORT_REGEX.test(text);
+const isImportOrExport = (text) => isImport(text) || isExport(text);
const tokenizeEsSyntax = (eat, value) => {
const index = value.indexOf(EMPTY_NEWLINE);
- const subvalue = value.slice(0, index);
+ const subvalue = index === -1 ? value : value.slice(0, index);
- if (isExport(subvalue) || isImport(subvalue)) {
+ if (isImportOrExport(subvalue)) {
return eat(subvalue)({
type: isExport(subvalue) ? "export" : "import",
value: subvalue,
@@ -44,6 +45,11 @@ const tokenizeEsSyntax = (eat, value) => {
}
};
+tokenizeEsSyntax.notInBlock = true;
+
+tokenizeEsSyntax.locator = (value /* , fromIndex*/) =>
+ isImportOrExport(value) ? -1 : 1;
+
const tokenizeEsComment = (eat, value) => {
const match = ES_COMMENT_REGEX.exec(value);
@@ -55,10 +61,6 @@ const tokenizeEsComment = (eat, value) => {
}
};
-/* c8 ignore next 2 */
-tokenizeEsSyntax.locator = (value /* , fromIndex*/) =>
- isExport(value) || isImport(value) ? -1 : 1;
-
tokenizeEsComment.locator = (value, fromIndex) => value.indexOf("{", fromIndex);
/** @import {Plugin, Settings} from "unified" */
diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js
index 06db9a182..55e43f8d8 100644
--- a/src/language-markdown/printer-markdown.js
+++ b/src/language-markdown/printer-markdown.js
@@ -432,7 +432,7 @@ function genericPrint(path, options, print) {
case "import":
case "export":
case "jsx":
- return node.value;
+ return node.value.trimEnd();
case "esComment":
return ["{/* ", node.value, " */}"];
case "math":
diff --git a/tests/format/mdx/import-export/__snapshots__/format.test.js.snap b/tests/format/mdx/import-export/__snapshots__/format.test.js.snap
new file mode 100644
index 000000000..4e0267731
--- /dev/null
+++ b/tests/format/mdx/import-export/__snapshots__/format.test.js.snap
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`esm.mdx format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+
+
+import { External} from './some/place.js'
+
+export const Local = properties =>
+
+export const Local = (properties) => (
+
+);
+
+An external component and a local one.
+
+=====================================output=====================================
+
+
+import { External } from "./some/place.js";
+
+export const Local = (properties) => (
+
+);
+
+export const Local = (properties) => (
+
+);
+
+An external component and a local one.
+
+================================================================================
+`;
+
+exports[`like-import-declaration.mdx format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+
+
+import .meta.resolve ( foo)
+
+import .meta; export {} from "./foo"
+
+import .meta; import {} from "./foo"
+
+import ("./foo")
+
+import {} from "./foo"; export {}
+
+=====================================output=====================================
+
+
+import .meta.resolve ( foo)
+
+import .meta; export {} from "./foo"
+
+import .meta; import {} from "./foo"
+
+import ("./foo")
+
+import {} from "./foo";
+export {};
+
+================================================================================
+`;
+
+exports[`list.mdx format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+- import is a word in lists
+- export is a word in lists, too!
+
+=====================================output=====================================
+- import is a word in lists
+- export is a word in lists, too!
+
+================================================================================
+`;
+
+exports[`paragraph.mdx format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+import is a word
+
+=====================================output=====================================
+import is a word
+
+================================================================================
+`;
+
+exports[`snippet: #0 format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+import is a word
+=====================================output=====================================
+import is a word
+
+================================================================================
+`;
diff --git a/tests/format/mdx/import-export/esm.mdx b/tests/format/mdx/import-export/esm.mdx
new file mode 100644
index 000000000..ffc965ee1
--- /dev/null
+++ b/tests/format/mdx/import-export/esm.mdx
@@ -0,0 +1,11 @@
+
+
+import { External} from './some/place.js'
+
+export const Local = properties =>
+
+export const Local = (properties) => (
+
+);
+
+An external component and a local one.
diff --git a/tests/format/mdx/import-export/format.test.js b/tests/format/mdx/import-export/format.test.js
new file mode 100644
index 000000000..5fc155a06
--- /dev/null
+++ b/tests/format/mdx/import-export/format.test.js
@@ -0,0 +1,7 @@
+runFormatTest(
+ {
+ importMeta: import.meta,
+ snippets: ["import is a word"],
+ },
+ ["mdx"],
+);
diff --git a/tests/format/mdx/import-export/like-import-declaration.mdx b/tests/format/mdx/import-export/like-import-declaration.mdx
new file mode 100644
index 000000000..e3df62078
--- /dev/null
+++ b/tests/format/mdx/import-export/like-import-declaration.mdx
@@ -0,0 +1,11 @@
+
+
+import .meta.resolve ( foo)
+
+import .meta; export {} from "./foo"
+
+import .meta; import {} from "./foo"
+
+import ("./foo")
+
+import {} from "./foo"; export {}
diff --git a/tests/format/mdx/import-export/list.mdx b/tests/format/mdx/import-export/list.mdx
new file mode 100644
index 000000000..8f6e6a138
--- /dev/null
+++ b/tests/format/mdx/import-export/list.mdx
@@ -0,0 +1,2 @@
+- import is a word in lists
+- export is a word in lists, too!
diff --git a/tests/format/mdx/import-export/paragraph.mdx b/tests/format/mdx/import-export/paragraph.mdx
new file mode 100644
index 000000000..14079ac15
--- /dev/null
+++ b/tests/format/mdx/import-export/paragraph.mdx
@@ -0,0 +1 @@
+import is a word