Fix MDX import and export parsing (#17996)

Co-authored-by: fisker <lionkay@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-09-29 23:46:36 +08:00
1 parent ae83caa9f6
commit 87d905e0bc
10 files changed
+199 -9

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
#### Fix `import` and `export` parsing (#17996 by @kovsu & @fisker)
<!-- prettier-ignore -->
```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!
```
+24 -1
View File
@@ -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;
+9 -7
View File
@@ -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" */
+1 -1
View File
@@ -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":
@@ -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======================================
<!-- https://mdxjs.com/docs/what-is-mdx/#esm -->
import { External} from './some/place.js'
export const Local = properties => <span style={{color: 'red'}} {...properties} />
export const Local = (properties) => (
<span style={{ color: "red" }} {...properties} />
);
An <External>external</External> component and a <Local>local one</Local>.
=====================================output=====================================
<!-- https://mdxjs.com/docs/what-is-mdx/#esm -->
import { External } from "./some/place.js";
export const Local = (properties) => (
<span style={{ color: "red" }} {...properties} />
);
export const Local = (properties) => (
<span style={{ color: "red" }} {...properties} />
);
An <External>external</External> component and a <Local>local one</Local>.
================================================================================
`;
exports[`like-import-declaration.mdx format 1`] = `
====================================options=====================================
parsers: ["mdx"]
printWidth: 80
| printWidth
=====================================input======================================
<!-- These are not \`import\`s -->
import .meta.resolve ( foo)
import .meta; export {} from "./foo"
import .meta; import {} from "./foo"
import ("./foo")
import {} from "./foo"; export {}
=====================================output=====================================
<!-- These are not \`import\`s -->
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
================================================================================
`;
+11
View File
@@ -0,0 +1,11 @@
<!-- https://mdxjs.com/docs/what-is-mdx/#esm -->
import { External} from './some/place.js'
export const Local = properties => <span style={{color: 'red'}} {...properties} />
export const Local = (properties) => (
<span style={{ color: "red" }} {...properties} />
);
An <External>external</External> component and a <Local>local one</Local>.
@@ -0,0 +1,7 @@
runFormatTest(
{
importMeta: import.meta,
snippets: ["import is a word"],
},
["mdx"],
);
@@ -0,0 +1,11 @@
<!-- These are not `import`s -->
import .meta.resolve ( foo)
import .meta; export {} from "./foo"
import .meta; import {} from "./foo"
import ("./foo")
import {} from "./foo"; export {}
+2
View File
@@ -0,0 +1,2 @@
- import is a word in lists
- export is a word in lists, too!
@@ -0,0 +1 @@
import is a word