Repository: prettier/prettier (JavaScript)
Date: 2020-12-10 11:35:59+00:00
Task type: Bug Fix

Issue:
Unstable JSX formatting with \u3000
**Prettier 1.19.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAE8OYLyYAUAlPgHybAA6UmmATnDAK722E110A8ADmZy480vAIa0A9GUAADMBgALAJZoAdL3oReqgCaiYolTEUwANnAC+07iPGYpgnhP6DiAbhrnXIADQhNR9GRQUXoNAHcABRCENGQQUQA3CEVtHxAAI3pRMABrJgBlMTBFKABzZBh6ZjhfeRgAWxMAdSUcIrh8mONFBOMATziwNFjfErQ4ehgIrNL60WQAM1ETcd8AKzQADwAhLNyC0Xq4ABkSuEXl1ZANzfyS0rMARWYIeAuVmpAxenH6OPTROk4CY0uoSjAmikFMgABwABl86gg4yaWV4cXUcF+CXOvgAji94NNNLEUKI0ABaKBwODaWlpRgExSMaaiWbzJBLD6+cb1RQVKqfND3J6E86cy6ffTpSHaaFIABMvkqokUJnuAGEIPU5nEoNBcSBmOMACqA0lcq4JaoASSgdNg+TA9EUvBgAEF7fkYH0zO9xuZzEA)
```sh
--parser babel
```

**Input:**
```jsx
const test = () => {
  return (
    <p>
      <span />　{this.props.data.title}　<span />
    </p>
  );
};
```

**Output:**
```jsx
const test = () => {
  return (
    <p>
      <span />
      　{this.props.data.title}　<span />
    </p>
  );
};

```

**Second Output:**
```jsx
const test = () => {
  return (
    <p>
      <span />　{this.props.data.title}　<span />
    </p>
  );
};

```

**Image:**
![Untitled](https://user-images.githubusercontent.com/23690145/69402850-06b1d680-0d34-11ea-891b-4a39dc14012a.gif)


**Expected behavior:**
Should be stable


Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/javascript/9866.md b/changelog_unreleased/javascript/9866.md
new file mode 100644
index 000000000000..72ebe84c41cd
--- /dev/null
+++ b/changelog_unreleased/javascript/9866.md
@@ -0,0 +1,29 @@
+#### Fix unstable JSX formatting with U+3000 (#9866 by @fisker)
+
+<!-- prettier-ignore -->
+```js
+// Input
+<p>
+  <span />　{this.props.data.title}　<span />
+  //----- ^ U+3000 --------------- ^ U+3000
+</p>
+
+// Prettier stable
+<p>
+  <span />
+  　{this.props.data.title}　<span />
+  //----- ^ U+3000 --------------- ^ U+3000
+</p>;
+
+// Prettier stable (second format)
+<p>
+  <span />　{this.props.data.title}　<span />
+  //----- ^ U+3000 --------------- ^ U+3000
+</p>;
+
+// Prettier master
+<p>
+  <span />　{this.props.data.title}　<span />
+  //----- ^ U+3000 --------------- ^ U+3000
+</p>;
+```
diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js
index c2a621527ad8..e2c0bca6a3e2 100644
--- a/src/language-js/print/jsx.js
+++ b/src/language-js/print/jsx.js
@@ -32,6 +32,7 @@ const {
   isBinaryish,
   hasComment,
   CommentCheckFlags,
+  trimJsxWhitespace,
 } = require("../utils");
 const pathNeedsParens = require("../needs-parens");
 const { willPrintOwnComments } = require("../comments");
@@ -373,9 +374,9 @@ function printJSXChildren(
       const directlyFollowedByMeaningfulText =
         next && isMeaningfulJSXText(next);
       if (directlyFollowedByMeaningfulText) {
-        const firstWord = rawText(next)
-          .trim()
-          .split(matchJsxWhitespaceRegex)[0];
+        const firstWord = trimJsxWhitespace(rawText(next)).split(
+          matchJsxWhitespaceRegex
+        )[0];
         children.push(
           separatorNoWhitespace(
             isFacebookTranslationTag,
diff --git a/src/language-js/utils.js b/src/language-js/utils.js
index fb3a2acc93ec..89675a4e91ec 100644
--- a/src/language-js/utils.js
+++ b/src/language-js/utils.js
@@ -737,6 +737,17 @@ const matchJsxWhitespaceRegex = new RegExp("([" + jsxWhitespaceChars + "]+)");
 const containsNonJsxWhitespaceRegex = new RegExp(
   "[^" + jsxWhitespaceChars + "]"
 );
+const trimJsxWhitespace = (text) =>
+  text.replace(
+    new RegExp(
+      "(?:^" +
+        matchJsxWhitespaceRegex.source +
+        "|" +
+        matchJsxWhitespaceRegex.source +
+        "$)"
+    ),
+    ""
+  );
 
 // Meaningful if it contains non-whitespace characters,
 // or it contains whitespace without a new line.
@@ -1571,4 +1582,5 @@ module.exports = {
   hasComment,
   getComments,
   CommentCheckFlags,
+  trimJsxWhitespace,
 };


Test patch:
diff --git a/tests/jsx/whitespace/jsfmt.spec.js b/tests/jsx/whitespace/jsfmt.spec.js
index 9d5fc32cf19e..e3e838ef6a4e 100644
--- a/tests/jsx/whitespace/jsfmt.spec.js
+++ b/tests/jsx/whitespace/jsfmt.spec.js
@@ -70,6 +70,18 @@ run_spec(
           );
         `,
       },
+      {
+        code: outdent`
+          <p>
+            <span />\u3000{this.props.data.title}\u3000<span />
+          </p>
+        `,
+        output: outdent`
+          <p>
+            <span />\u3000{this.props.data.title}\u3000<span />
+          </p>;
+        `,
+      },
     ].map((test) => ({ ...test, output: test.output + "\n" })),
   },
   ["flow", "typescript"]