Repository: prettier/prettier (JavaScript)

Issue:
Inline React components in MDX cause line lengths to exceed print-width
<!--

BEFORE SUBMITTING AN ISSUE:

1.  Search for your issue on GitHub: https://github.com/prettier/prettier/issues
    A large number of opened issues are duplicates of existing issues.
    If someone has already opened an issue for what you are experiencing,
    you do not need to open a new issue — please add a 👍 reaction to the
    existing issue instead.

2.  We get a lot of requests for adding options, but Prettier is
    built on the principle of being opinionated about code formatting.
    This means we have a very high bar for adding new options.
    Find out more: https://prettier.io/docs/en/option-philosophy.html

Tip! Don't write this stuff manually.

1. Go to https://prettier.io/playground
2. Paste your code and set options
3. Press the "Report issue" button in the lower right

-->

**Prettier 2.1.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAeAogD3gJygQwBsAZASygGsACACxzgDMBeAHRBphgAckB6X-AHcAzgDp8AW3wAvaKMgTekACZwupLnELk4vNgD4AggHUAylQDCEVQAUNWnal5ZcBEuQr6qpYVXxVIWHIAVwhg31VtADc4HABPPyhlKnpCOHxhOD9gmAgpGFJoKkycKNIwLJgafBg-UmVfYQkICCqqVS5CCDiJBBgxKgBNMID8KG9YOABzHBqsgGYcZK58HBgE3IhCX3JcxITheC4qCAYqOLCcFK10zKouHAgK4V8IK4vg0fHwrKtbe20UCyGUSVAQygAtLkIeDilscoUoAAaCZgQjBZTkKZUADiNAghyoAEkoMJNGBcld4IdfGNksJgqRqaIQEiQBAuAVoMJkKBVo9BDZVggeSgiIJ8HEeWyAEazMAUOAwUwrMBY5AwHDBOBsuASGVwZSqZTEMZTYL4KZwABib3yBSgU2QIHwOQgrPYMAkhGMNCZcDJ+AqphFTNIZXWzrALw95BKMBssymUmQDCImTZACthJgAELyxXKyRwMhA1PpnUgbOYUxYtIARVC8HL20rKxwJWdEmUmA9DwJcGMsy4zoeAdiMQ9AEcm3BExzRS7hBCgYbDR76DPSPRE5aU0g0622ZkJKQNVrK8I63BG604C2MyAYPgZcZ6lVkAAmNma-CkQFTFYEj7iAAYAKwej8AAqL6ioej5RNqJKqLAphgDgGgwIYSSmOsaQPm2GGwG+ygfkgACMAAMlEAL40UAA)
```sh
--parser mdx
--print-width 100
```

**Input:**
```mdx
<ExternalLink href="http://aws.amazon.com/codepipeline/">AWS CodePipeline</ExternalLink> is a continuous delivery and release automation service that aids smooth deployments. You can integrate 3rd party tools into any step of your release process or you can use CodePipeline as an end-to-end solution, including Ghost Inspector tests and suites.
```

**Output:**
```mdx
<ExternalLink href="http://aws.amazon.com/codepipeline/">AWS CodePipeline</ExternalLink> is a continuous
delivery and release automation service that aids smooth deployments. You can integrate 3rd party tools
into any step of your release process or you can use CodePipeline as an end-to-end solution, including
Ghost Inspector tests and suites.

```

**Expected behavior:**

Here the output optically looks correct, however if you view it in the playground or any editor that has a line length “ruler” you can see that each line is actually longer than 100 characters.

```
❯ echo '<ExternalLink href="http://aws.amazon.com/codepipeline/">AWS CodePipeline</ExternalLink> is a continuous' | wc
       1       6     105
```

This causes a bit of a problem if you have word wrapping on in your editor.

<img width="1014" alt="Screen Shot 2020-10-26 at 1 58 54 PM" src="https://user-images.githubusercontent.com/4727/97228071-9c9b8000-1793-11eb-94c5-2669e9d7b01c.png">

If I remove the inline component it works fine, so the bug seems to have something to do with inline React components.

Thanks!
Refactor markdown `paragraph` print
## Description

<!-- Please provide a brief summary of your changes: -->

Fix #9503

## Checklist

<!-- Please ensure you’ve done all of these things (if applicable). -->
<!-- You can replace the `[ ]` with `[x]` to mark each task as done. -->

- [x] I’ve added tests to confirm my change works.
- [ ] (If changing the API or CLI) I’ve documented the changes I’ve made (in the `docs/` directory).
- [x] (If the change is user-facing) I’ve added my changes to `changelog_unreleased/*/XXXX.md` file following `changelog_unreleased/TEMPLATE.md`.
- [x] I’ve read the [contributing guidelines](https://github.com/prettier/prettier/blob/master/CONTRIBUTING.md).

<!-- Please DO NOT remove the playground link -->

**✨[Try the playground for this PR](https://prettier.io/playground-redirect)✨**



Hints:
Thanks for reporting this. If you're interested in fixing it, use the "show doc" checkbox in the playground to check the intermediate representation that Prettier builds. It's described [here](https://github.com/prettier/prettier/blob/master/commands.md). The `fill` command has [certain expectations](https://github.com/prettier/prettier/blob/099c5fb7302021c894e38f53bb09909d45870502/src/document/doc-printer.js#L366-L367) about its arguments, a violation of which may be the cause of this bug.

You can play with the IR in this [modified playground](https://prettier-doc.netlify.app/playground/#N4Igxg9gdgLgprEAuc0wEMYAoDaAdKAgMwEsAbM3AgAmoHMAnCAVwActIoNt8pba8IADwBnCAFs4MABYkodALRkSAazgK5yqOsjjW0BDEEAaGvy1xTffoID0APkFmAugEpXVgSGpkIDOOImZhae1IIkrCLMgSAEbqEE0ugMACYhBMTklLy0jCzsnNxU1l6iElKy8kqq6ppyOhL62rBBJT71oV4OTtZuHsEdZoK+-jGh6dbhkdE98UNQbiDGIBCsMCTQIsigyUwA7gAKyQhbKOhke+gAnlvLAEYM6GBqMADKrE9ydMgwDMyWIACdzgKRSIIAMuh5Mx0HQ4AAxPziTDreTIEDoZgwCBLEDSGDiMgAdVk8BEHzAcFeJxI6wAbrSruiwCJbiA5CI4AwYAdHnRkcgiOdOcsAFYiAAeACFHs8pK90JJwfVBcKAeKJa8vmQ4ABFZgQeCqsgikAfBichjo1j+GDrLkKFIQMC4m1yGBEkgpGTIAAsAFZljaIJyiY9WNb-Ja6XBcQBHA3wXmrU4YkQKbQgkG4-wJkj+XmwgVIIUmgGc8QkH5-cvavWJ2MltXLGDoO6e73SZAAJhbj3IXwAwhJi4CRP7ccxOQAVNunUumun-ACSUDBsFeYAYERgAEE168YFcdcbOQBfM9AA).
I have a little bit of experience with ASTs/AST-like things so I took a look. I simplifed down the example and maybe noticed some things… it’s hard to tell because I’m not exactly what “normal” is supposed to look like.

I used two strings:

```
<em>hey</em>

<em>hey</em> is a continuous
```

<img width="568" alt="Screen Shot 2020-10-27 at 4 49 34 PM" src="https://user-images.githubusercontent.com/4727/97374390-67675e80-1875-11eb-9d5c-468e831aebec.png">

The HTML definitely seems to be parsed differently when there is markdown after it. It could be a concidence but those extra empty strings seem to match up to the amount of characters that the print width is exceeded by…
I guess the prefixed space is the culprit. It should be `line, "is"` instead.
@thorn0 You are right about this, https://github.com/prettier/prettier/pull/9736#discussion_r527917647, it's bug in `normalizeDoc`.
@thorn0 Can you review this, when you got time? This is actually quite big change, because it called unsafe `normalizeDoc` on "Root", and I removed it.  But maybe `normalizeDoc` was safe before we extract to `doc-utils.js`
Ooops, I keep forgetting about this one...
The doc for #9503 still doesn't look right:
```js
[
  fill([
    conditionalGroup([
      group([/*...*/]),
      group([/*...*/]),
    ]),
    " ", // <--- should be `line`
    "is",
    line,
// ...
```
Yes, this is a big one. My research plan so far:

## MDX: line break inserted between elements and text in JSX blocks

```<asdf/>zxcv```

- Prettier 2.2.1 parses it all as JSX (not Markdown) and prints line breaks the way it normally does for JSX.
- That's probably not the expected output. That's why #6961 was opened.
- Also there is a `fill` bug that causes v2.2.1 to print `<Hello></Hello><Hello></Hello>123` without that break.
  ```js
  [
    fill([
      group(["<Hello", indent([]), ">"]),
      "</Hello>", // <--- Problem! Odd-indexed elements must be line breaks.
      hardlineWithoutBreakParent,
      breakParent,
      group(["<Hello", indent([]), ">"]),
      "</Hello>",
      softline,
      "123"
    ]),
    hardline,
  ]
  ```
- Things to look into:
  1. The `fill` bug
     1. Is the bug in #9503 same or different?
     2. How did it work in older versions?
     3. Was `normalizeDoc` safe before it got extracted to `doc-utils`?
     4. Manipulations with `normalizeDoc` and `cleanDoc` (in particular, PR #9847) look like they might affect more things in Markdown than just MDX. What else can be affected?
  2. What actually is the expected output?
     1. Is the whitespace between the tag and the text significant?
     2. Should the text be parsed as JSX or as Markdown?


Patch:
diff --git a/src/language-markdown/print-paragraph.js b/src/language-markdown/print-paragraph.js
new file mode 100644
--- /dev/null
+++ b/src/language-markdown/print-paragraph.js
@@ -0,0 +1,20 @@
+import { fill } from "../document/builders.js";
+import { normalizeDoc } from "../document/utils.js";
+
+/**
+ * @typedef {import("../common/ast-path.js").default} AstPath
+ * @typedef {import("../document/builders.js").Doc} Doc
+ */
+
+/**
+ * @param {AstPath} path
+ * @param {*} options
+ * @param {*} print
+ * @returns {Doc}
+ */
+function printParagraph(path, options, print) {
+  const parts = path.map(print, "children");
+  return normalizeDoc(fill(parts));
+}
+
+export { printParagraph };
diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js
--- a/src/language-markdown/printer-markdown.js
+++ b/src/language-markdown/printer-markdown.js
@@ -2,7 +2,6 @@ import collapseWhiteSpace from "collapse-white-space";
 
 import {
   align,
-  fill,
   group,
   hardline,
   indent,
@@ -11,7 +10,7 @@ import {
   markAsRoot,
   softline,
 } from "../document/builders.js";
-import { normalizeDoc, replaceEndOfLine } from "../document/utils.js";
+import { replaceEndOfLine } from "../document/utils.js";
 import getMaxContinuousCount from "../utils/get-max-continuous-count.js";
 import getMinNotPresentContinuousCount from "../utils/get-min-not-present-continuous-count.js";
 import getPreferredQuote from "../utils/get-preferred-quote.js";
@@ -23,6 +22,7 @@ import getVisitorKeys from "./get-visitor-keys.js";
 import { locEnd, locStart } from "./loc.js";
 import { insertPragma } from "./pragma.js";
 import { printTable } from "./print/table.js";
+import { printParagraph } from "./print-paragraph.js";
 import preprocess from "./print-preprocess.js";
 import { printWhitespace } from "./print-whitespace.js";
 import {
@@ -71,11 +71,9 @@ function genericPrint(path, options, print) {
       if (node.children.length === 0) {
         return "";
       }
-      return [normalizeDoc(printRoot(path, options, print)), hardline];
+      return [printRoot(path, options, print), hardline];
     case "paragraph":
-      return printChildren(path, options, print, {
-        postprocessor: fill,
-      });
+      return printParagraph(path, options, print);
     case "sentence":
       return printChildren(path, options, print);
     case "word": {
@@ -559,8 +557,7 @@ function printRoot(path, options, print) {
 }
 
 function printChildren(path, options, print, events = {}) {
-  const { postprocessor = (parts) => parts, processor = () => print() } =
-    events;
+  const { processor = print } = events;
 
   const parts = [];
 
@@ -586,7 +583,7 @@ function printChildren(path, options, print, events = {}) {
     }
   }, "children");
 
-  return postprocessor(parts);
+  return parts;
 }
 
 function printIgnoreComment(node) {


Test patch:
diff --git a/tests/format/markdown/code/__snapshots__/jsfmt.spec.js.snap b/tests/format/markdown/code/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/markdown/code/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/markdown/code/__snapshots__/jsfmt.spec.js.snap
@@ -339,9 +339,9 @@ const createCredentialDefaultArgs = {
     timeout: 60000,
     challenge: new Uint8Array([
       // must be a cryptographically random number sent from a server
-      0x8c, 0x0a, 0x26, 0xff, 0x22, 0x91, 0xc1, 0xe9, 0xb9, 0x4e, 0x2e, 0x17, 0x1a,
-      0x98, 0x6a, 0x73, 0x71, 0x9d, 0x43, 0x48, 0xd5, 0xa7, 0x6a, 0x15, 0x7e, 0x38,
-      0x94, 0x52, 0x77, 0x97, 0x0f, 0xef,
+      0x8c, 0x0a, 0x26, 0xff, 0x22, 0x91, 0xc1, 0xe9, 0xb9, 0x4e, 0x2e, 0x17,
+      0x1a, 0x98, 0x6a, 0x73, 0x71, 0x9d, 0x43, 0x48, 0xd5, 0xa7, 0x6a, 0x15,
+      0x7e, 0x38, 0x94, 0x52, 0x77, 0x97, 0x0f, 0xef,
     ]).buffer,
   },
 };
@@ -381,7 +381,8 @@ proseWrap: "always"
         105deg,
         rgb(255 255 255 / 20%) 39%,
         rgb(51 56 57 / 100%) 96%
-      ) center center / 400px 200px no-repeat,
+      )
+      center center / 400px 200px no-repeat,
     url(big-star.png) center no-repeat,
     rebeccapurple;
 }
@@ -415,7 +416,8 @@ background-image: linear-gradient(
     to bottom,
     rgb(255 255 0 / 50%),
     rgb(0 0 255 / 50%)
-  ), url("catfront.png");
+  ),
+  url("catfront.png");
 \`\`\`
 
 ================================================================================
@@ -455,7 +457,8 @@ width: 400px;
       rgb(131 58 180 / 100%) 0%,
       rgb(253 29 29 / 60%) 60%,
       rgb(252 176 69 / 100%) 100%
-    ), radial-gradient(circle, rgb(255 255 255 / 100%) 0%, rgb(0 0 0 / 100%) 28%);
+    ),
+    radial-gradient(circle, rgb(255 255 255 / 100%) 0%, rgb(0 0 0 / 100%) 28%);
   border: 20px dashed black;
   padding: 20px;
   width: 400px;
@@ -495,8 +498,8 @@ background-position:
 .multi-bg-example {
   width: 100%;
   height: 400px;
-  background-image: url(firefox.png), url(bubbles.png), linear-gradient(to right, rgb(30
-          75 115 / 100%), rgb(255 255 255 / 0%));
+  background-image: url(firefox.png), url(bubbles.png),
+    linear-gradient(to right, rgb(30 75 115 / 100%), rgb(255 255 255 / 0%));
   background-repeat: no-repeat, no-repeat, no-repeat;
   background-position:
     bottom right,
@@ -536,7 +539,8 @@ proseWrap: "always"
       217deg,
       rgb(255 0 0 / 80%),
       rgb(255 0 0 / 0%) 70.71%
-    ), linear-gradient(127deg, rgb(0 255 0 / 80%), rgb(0 255 0 / 0%) 70.71%),
+    ),
+    linear-gradient(127deg, rgb(0 255 0 / 80%), rgb(0 255 0 / 0%) 70.71%),
     linear-gradient(336deg, rgb(0 0 255 / 80%), rgb(0 0 255 / 0%) 70.71%);
 }
 \`\`\`
@@ -595,7 +599,8 @@ proseWrap: "always"
         circle at 93.3% 75%,
         rgb(0 255 0 / 50%),
         rgb(0 255 0 / 0%) 70.71%
-      ) beige;
+      )
+      beige;
   border-radius: 50%;
 }
 \`\`\`
@@ -653,7 +658,8 @@ rgb(255 255 0 / 50%) 120px,
       rgb(75 0 130 / 50%) 240px,
       rgb(238 130 238 / 50%) 280px,
       rgb(255 0 0 / 50%) 300px
-    ), repeating-linear-gradient(
+    ),
+    repeating-linear-gradient(
       -190deg,
       rgb(255 0 0 / 50%) 30px,
       rgb(255 153 0 / 50%) 60px,
@@ -663,8 +669,18 @@ rgb(255 255 0 / 50%) 120px,
       rgb(75 0 130 / 50%) 180px,
       rgb(238 130 238 / 50%) 210px,
       rgb(255 0 0 / 50%) 230px
-    ), repeating-linear-gradient(23deg, red 50px, orange 100px, yellow 150px, green
-        200px, blue 250px, indigo 300px, violet 350px, red 370px);
+    ),
+    repeating-linear-gradient(
+      23deg,
+      red 50px,
+      orange 100px,
+      yellow 150px,
+      green 200px,
+      blue 250px,
+      indigo 300px,
+      violet 350px,
+      red 370px
+    );
 }
 \`\`\`
 
@@ -763,7 +779,8 @@ transparent 69px,
       transparent 116px,
       rgb(255 206 0 / 25%) 116px,
       rgb(255 206 0 / 25%) 166px
-    ), repeating-linear-gradient(
+    ),
+    repeating-linear-gradient(
       0deg,
       transparent,
       transparent 50px,
@@ -777,15 +794,21 @@ transparent 69px,
       transparent 116px,
       rgb(255 206 0 / 25%) 116px,
       rgb(255 206 0 / 25%) 166px
-    ), repeating-linear-gradient(
+    ),
+    repeating-linear-gradient(
       -45deg,
       transparent,
       transparent 5px,
       rgb(143 77 63 / 25%) 5px,
       rgb(143 77 63 / 25%) 10px
-    ), repeating-linear-gradient(45deg, transparent, transparent 5px, rgb(
-          143 77 63 / 25%
-        ) 5px, rgb(143 77 63 / 25%) 10px);
+    ),
+    repeating-linear-gradient(
+      45deg,
+      transparent,
+      transparent 5px,
+      rgb(143 77 63 / 25%) 5px,
+      rgb(143 77 63 / 25%) 10px
+    );
 
   background: repeating-linear-gradient(
       90deg,
@@ -795,7 +818,8 @@ transparent 69px,
       rgb(255 127 0 / 25%) 63px 69px,
       transparent 69px 116px,
       rgb(255 206 0 / 25%) 116px 166px
-    ), repeating-linear-gradient(
+    ),
+    repeating-linear-gradient(
       0deg,
       transparent 0 50px,
       rgb(255 127 0 / 25%) 50px 56px,
@@ -803,12 +827,17 @@ transparent 69px,
       rgb(255 127 0 / 25%) 63px 69px,
       transparent 69px 116px,
       rgb(255 206 0 / 25%) 116px 166px
-    ), repeating-linear-gradient(
+    ),
+    repeating-linear-gradient(
       -45deg,
       transparent 0 5px,
       rgb(143 77 63 / 25%) 5px 10px
-    ), repeating-linear-gradient(45deg, transparent 0 5px, rgb(143 77 63 / 25%) 5px
-        10px);
+    ),
+    repeating-linear-gradient(
+      45deg,
+      transparent 0 5px,
+      rgb(143 77 63 / 25%) 5px 10px
+    );
 }
 \`\`\`
 
@@ -845,7 +874,8 @@ div {
       #000 0.25turn 0.5turn,
       #fff 0.5turn 0.75turn,
       #000 0.75turn
-    ) top left / 25% 25% repeat;
+    )
+    top left / 25% 25% repeat;
   border: 1px solid;
 }
 \`\`\`
@@ -877,12 +907,10 @@ filter: none;
 =====================================output=====================================
 \`\`\`css
 img {
-  filter: drop-shadow(2px 2px 0 hsl(300deg 100% 50%)) drop-shadow(
-      -2px -2px 0 hsl(210deg 100% 50%)
-    )
-    drop-shadow(2px 2px 0 hsl(120deg 100% 50%)) drop-shadow(
-      -2px -2px 0 hsl(30deg 100% 50%)
-    );
+  filter: drop-shadow(2px 2px 0 hsl(300deg 100% 50%))
+    drop-shadow(-2px -2px 0 hsl(210deg 100% 50%))
+    drop-shadow(2px 2px 0 hsl(120deg 100% 50%))
+    drop-shadow(-2px -2px 0 hsl(30deg 100% 50%));
 }
 img + img {
   filter: none;
@@ -915,8 +943,8 @@ border: 1px solid blue;
 \`\`\`css
 #MDN-logo {
   border: 1px solid blue;
-  filter: drop-shadow(5px 5px 0 red) hue-rotate(180deg) drop-shadow(5px 5px 0
-        red);
+  filter: drop-shadow(5px 5px 0 red) hue-rotate(180deg)
+    drop-shadow(5px 5px 0 red);
 }
 \`\`\`
 
@@ -953,7 +981,8 @@ font-style: normal;
   font-family: "HeydingsControlsRegular";
   src: url("fonts/heydings_controls-webfont.eot");
   src:
-    url("fonts/heydings_controls-webfont.eot?#iefix") format("embedded-opentype"),
+    url("fonts/heydings_controls-webfont.eot?#iefix")
+      format("embedded-opentype"),
     url("fonts/heydings_controls-webfont.woff") format("woff"),
     url("fonts/heydings_controls-webfont.ttf") format("truetype");
   font-weight: normal;
@@ -991,7 +1020,8 @@ proseWrap: "always"
 @font-face {
   font-family: "Bungee Spice";
   src:
-    url("https://fonts.googleapis.com/css2?family=Bungee+Spice") tech(color-COLRv1),
+    url("https://fonts.googleapis.com/css2?family=Bungee+Spice")
+      tech(color-COLRv1),
     url("Bungee-fallback.otf") format("opentype");
 }
 \`\`\`
@@ -1025,7 +1055,8 @@ grid-auto-columns: min-content max-content auto;
 grid-auto-columns: 100px 150px 390px;
 grid-auto-columns: 10% 33.3%;
 grid-auto-columns: 0.5fr 3fr 1fr;
-grid-auto-columns: minmax(100px, auto) minmax(max-content, 2fr) minmax(20%, 80vmax);
+grid-auto-columns: minmax(100px, auto) minmax(max-content, 2fr)
+  minmax(20%, 80vmax);
 grid-auto-columns: 100px minmax(100px, auto) 10% 0.5fr fit-content(400px);
 \`\`\`
 
@@ -1053,10 +1084,10 @@ proseWrap: "always"
 =====================================output=====================================
 \`\`\`css
 @import url("gridy.css") supports(display: grid) screen and (max-width: 400px);
-@import url("flexy.css") supports(not (display: grid) and (display: flex)) screen
-  and (max-width: 400px);
-@import url("whatever.css") supports((selector(h2 > p)) and
-    (font-tech(color-COLRv1)));
+@import url("flexy.css") supports(not (display: grid) and (display: flex))
+  screen and (max-width: 400px);
+@import url("whatever.css")
+supports((selector(h2 > p)) and (font-tech(color-COLRv1)));
 \`\`\`
 
 ================================================================================
@@ -1079,7 +1110,8 @@ mask-image: image(url(mask.png), skyblue),                          linear-gradi
 =====================================output=====================================
 \`\`\`css
 /* Multiple values */
-mask-image: image(url(mask.png), skyblue), linear-gradient(rgb(0 0 0 / 100%), transparent);
+mask-image: image(url(mask.png), skyblue),
+  linear-gradient(rgb(0 0 0 / 100%), transparent);
 \`\`\`
 
 ================================================================================
@@ -1107,10 +1139,8 @@ padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px) env(
 =====================================output=====================================
 \`\`\`css
 body {
-  padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px) env(
-      safe-area-inset-bottom,
-      20px
-    ) env(safe-area-inset-left, 20px);
+  padding: env(safe-area-inset-top, 20px) env(safe-area-inset-right, 20px)
+    env(safe-area-inset-bottom, 20px) env(safe-area-inset-left, 20px);
 }
 \`\`\`
 
@@ -1143,10 +1173,8 @@ p {
 p {
   width: 300px;
   border: 2px solid red;
-  padding: env(safe-area-inset-top, 50px) env(safe-area-inset-right, 50px) env(
-      safe-area-inset-bottom,
-      50px
-    ) env(SAFE-AREA-INSET-LEFT, 50px);
+  padding: env(safe-area-inset-top, 50px) env(safe-area-inset-right, 50px)
+    env(safe-area-inset-bottom, 50px) env(SAFE-AREA-INSET-LEFT, 50px);
 }
 \`\`\`
 
@@ -1188,24 +1216,8 @@ proseWrap: "always"
 =====================================output=====================================
 \`\`\`css
 #example-element:focus {
-  transform: rotate3d(1, 1, 1, 30deg) matrix3d(
-      1,
-      0,
-      0,
-      0,
-      0,
-      1,
-      6,
-      0,
-      0,
-      0,
-      1,
-      0,
-      50,
-      100,
-      0,
-      1.1
-    );
+  transform: rotate3d(1, 1, 1, 30deg)
+    matrix3d(1, 0, 0, 0, 0, 1, 6, 0, 0, 0, 1, 0, 50, 100, 0, 1.1);
 }
 \`\`\`
 
@@ -1257,9 +1269,9 @@ U+0152-0153, U+02BB-02BC, U+02C6,
       local("Montserrat Regular"),
       local("Montserrat-Regular"),
       url("fonts/montserrat-regular.woff2") format("woff2");
-    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
-      U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
-      U+2212, U+2215, U+FEFF, U+FFFD;
+    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+      U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212,
+      U+2215, U+FEFF, U+FFFD;
   }
 }
 \`\`\`
diff --git a/tests/format/mdx/mdx/__snapshots__/jsfmt.spec.js.snap b/tests/format/mdx/mdx/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/mdx/mdx/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/mdx/mdx/__snapshots__/jsfmt.spec.js.snap
@@ -345,6 +345,37 @@ and as you can see_ once you close the italics, it will break incorrectly when p
 ================================================================================
 `;
 
+exports[`issue-9503.mdx - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+<ExternalLink href="http://example.com">Prettier</ExternalLink> is an opinionated-code-formatter-that-support-many-languages-and-integrate-with-most-editors
+
+=====================================output=====================================
+<ExternalLink href="http://example.com">Prettier</ExternalLink> is an
+opinionated-code-formatter-that-support-many-languages-and-integrate-with-most-editors
+
+================================================================================
+`;
+
+exports[`issue-9503.mdx format 1`] = `
+====================================options=====================================
+parsers: ["mdx"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<ExternalLink href="http://example.com">Prettier</ExternalLink> is an opinionated-code-formatter-that-support-many-languages-and-integrate-with-most-editors
+
+=====================================output=====================================
+<ExternalLink href="http://example.com">Prettier</ExternalLink> is an
+opinionated-code-formatter-that-support-many-languages-and-integrate-with-most-editors
+
+================================================================================
+`;
+
 exports[`jsx.mdx - {"semi":false} format 1`] = `
 ====================================options=====================================
 parsers: ["mdx"]
@@ -416,7 +447,8 @@ A {/* JS-style comment */} comment.
 </Hello>
 <Hello>
   test <World /> test
-</Hello>123
+</Hello>
+123
 
 ---
 
@@ -518,7 +550,8 @@ A {/* JS-style comment */} comment.
 </Hello>
 <Hello>
   test <World /> test
-</Hello>123
+</Hello>
+123
 
 ---
 
diff --git a/tests/format/mdx/mdx/issue-9503.mdx b/tests/format/mdx/mdx/issue-9503.mdx
new file mode 100644
--- /dev/null
+++ b/tests/format/mdx/mdx/issue-9503.mdx
@@ -0,0 +1 @@
+<ExternalLink href="http://example.com">Prettier</ExternalLink> is an opinionated-code-formatter-that-support-many-languages-and-integrate-with-most-editors