Repository: prettier/prettier (JavaScript)
Date: 2020-10-16 10:34:29+00:00
Task type: Bug Fix

Issue:
Invalid strings in "html" different from other tags
**Prettier 2.1.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzCEAGAdbBXYABwCc4YYBLOYgX1wIAYGA2AVhswG5soALGALYAbHPiKlyVWvWBM2HblADmxAIaFeARxEySZStTpi57LjwGriAawAmEAO5RRBPZMMyTCngHpvAAjAAZ0DncX0pI0YWU0UedCxdCQNaABJgeJoSD2iaN2IzPkEdMVdkmjSMrOMcvIKVdS1ilySIioxM4mz5WsULaztHUNLW9PaqqO7kgt8A4KGWwzaIDq72Hp4NqHjQzyWV6smpAv5hHZy98dkaqcUZ+o1tM-kLzoO1m-NLWwcnVfLR5aXTzrKAzIIhRLhRYA-YTd5HTggAA0IAghEo0ECyFAlmIDgACpYEFiUKohPZVABPLEogBGajAVjIAGVCKowBRlMgYMQ8HAUXABLS4DYbCKADKqZR4VRKOAAMQgxAski5pLwMAgyJAJyEAHVeBR4IE2WA4MziUaKAA3I2U5DgYLazmBagwfFqJQWZCoMmulEAK0CAA8AEIMpkwZmqARwcWcuA+v38kBB4PMzlKIRwACKeAg8CTQn9IDZxFdxAdtNUwqE2pInJgeooNhgvGQAA4GCiSBBXXq1IQHXoK9bEyjNPn4B60SSQKpAgBaKBwEUi7WkScUUge2XepC+4sp10CCjc3nHzPZvMFxMH5MomA15ut9tIABMj7UFCEmYAwhAAj7iAcCBKw2p4K6AAqNYkoeJbWnyACSUBirAzJgMQFDogAgqhzIwJS2ZFq6NA0EAA)
```sh
--parser babel
```

**Input:**
```jsx
foo`\u{prettier}\u{0065}`;
html`\u{prettier}\u{0065}`;
graphql`\u{prettier}\u{0065}`;
markdown`\u{prettier}\u{0065}`;
// css`\u{prettier}\u{0065}`;

foo`\u{prettier}${foo}pr\u{0065}ttier`;
html`\u{prettier}${foo}pr\u{0065}ttier`;
graphql`\u{prettier}${foo}pr\u{0065}ttier`;
markdown`\u{prettier}${foo}pr\u{0065}ttier`;
// css`\u{prettier}${foo}pr\u{0065}ttier`;


foo`\u{0065}${foo}pr\u{0065}ttier`;
html`\u{0065}${foo}pr\u{0065}ttier`;
// graphql`\u{0065}${foo}pr\u{0065}ttier`;
markdown`\u{0065}${foo}pr\u{0065}ttier`;
// css`\u{prettier}${foo}pr\u{0065}ttier`;
```

**Output:**
```jsx
foo`\u{prettier}\u{0065}`;
html``;
graphql`\u{prettier}\u{0065}`;
markdown`
\u{prettier}\u{0065}
`;
// css`\u{prettier}\u{0065}`;

foo`\u{prettier}${foo}pr\u{0065}ttier`;
html`null${foo}prettier`;
graphql`\u{prettier}${foo}pr\u{0065}ttier`;
markdown`\u{prettier}${foo}pr\u{0065}ttier`;
// css`\u{prettier}${foo}pr\u{0065}ttier`;

foo`\u{0065}${foo}pr\u{0065}ttier`;
html`e${foo}prettier`;
// graphql`\u{0065}${foo}pr\u{0065}ttier`;
markdown`\u{0065}${foo}pr\u{0065}ttier`;
// css`\u{prettier}${foo}pr\u{0065}ttier`;

```

**Expected behavior:**



Issue discussion:
`markdown` is a little different too, it didn't use `TemplateElement.cooked`

Solution patch:
diff --git a/changelog_unreleased/javascript/pr-9431.md b/changelog_unreleased/javascript/pr-9431.md
new file mode 100644
index 000000000000..b3233bf01c14
--- /dev/null
+++ b/changelog_unreleased/javascript/pr-9431.md
@@ -0,0 +1,21 @@
+#### Keep html and markdown invalid template literals as it is (#9431 by @fisker)
+
+<!-- prettier-ignore -->
+```js
+// Input
+foo = html`<div>\u{prettier}</div>`;
+foo = html`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = markdown`# \u{prettier}\u{0065}`;
+
+// Prettier stable
+foo = html``;
+foo = html`null${foo}prettier`;
+foo = markdown`
+# \u{prettier}\u{0065}
+`;
+
+// Prettier master
+foo = html`<div>\u{prettier}</div>`;
+foo = html`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = markdown`# \u{prettier}\u{0065}`;
+```
diff --git a/src/language-js/embed.js b/src/language-js/embed.js
index 371642fb569d..eb7aba3daccc 100644
--- a/src/language-js/embed.js
+++ b/src/language-js/embed.js
@@ -24,6 +24,12 @@ function embed(path, print, textToDoc, options) {
 
   switch (node.type) {
     case "TemplateLiteral": {
+      // Bail out if any of the quasis have an invalid escape sequence
+      // (which would make the `cooked` value be `null`)
+      if (hasInvalidCookedValue(node)) {
+        return;
+      }
+
       const isCss = [
         isStyledJsx,
         isStyledComponents,
@@ -69,7 +75,6 @@ function embed(path, print, textToDoc, options) {
         const expressionDocs = path.map(printTemplateExpression, "expressions");
 
         const numQuasis = node.quasis.length;
-
         if (numQuasis === 1 && node.quasis[0].value.raw.trim() === "") {
           return "``";
         }
@@ -82,12 +87,6 @@ function embed(path, print, textToDoc, options) {
           const isLast = i === numQuasis - 1;
           const text = templateElement.value.cooked;
 
-          // Bail out if any of the quasis have an invalid escape sequence
-          // (which would make the `cooked` value be `null` or `undefined`)
-          if (typeof text !== "string") {
-            return null;
-          }
-
           const lines = text.split("\n");
           const numLines = lines.length;
           const expressionDoc = expressionDocs[i];
@@ -166,6 +165,9 @@ function embed(path, print, textToDoc, options) {
     }
 
     case "TemplateElement": {
+      if (hasInvalidCookedValue(parent)) {
+        return;
+      }
       /**
        * md`...`
        * markdown`...`
@@ -662,4 +664,8 @@ function printHtmlTemplateLiteral(
   );
 }
 
+function hasInvalidCookedValue({ quasis }) {
+  return quasis.some(({ value: { cooked } }) => cooked === null);
+}
+
 module.exports = embed;


Test patch:
diff --git a/tests/js/multiparser-invalid/__snapshots__/jsfmt.spec.js.snap b/tests/js/multiparser-invalid/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..437f6e376bc1
--- /dev/null
+++ b/tests/js/multiparser-invalid/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,75 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`text.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+foo = foo\`\\u{prettier}\\u{0065}\`;
+foo = html\`\\u{prettier}\\u{0065}\`;
+foo = graphql\`\\u{prettier}\\u{0065}\`;
+foo = markdown\`\\u{prettier}\\u{0065}\`;
+foo = css\`\\u{prettier}\\u{0065}\`;
+foo = /* HTML */\`\\u{prettier}\\u{0065}\`;
+foo = /* GraphQL */\`\\u{prettier}\\u{0065}\`;
+
+foo = foo\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = html\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = graphql\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = markdown\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = css\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = /* HTML */\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = /* GraphQL */\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+
+foo = foo\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = html\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = graphql\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = markdown\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = css\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = /* HTML */\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = /* GraphQL */\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+
+foo = foo\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = html\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = graphql\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = markdown\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = css\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = /* HTML */\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = /* GraphQL */\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+
+=====================================output=====================================
+foo = foo\`\\u{prettier}\\u{0065}\`;
+foo = html\`\\u{prettier}\\u{0065}\`;
+foo = graphql\`\\u{prettier}\\u{0065}\`;
+foo = markdown\`\\u{prettier}\\u{0065}\`;
+foo = css\`\\u{prettier}\\u{0065}\`;
+foo = /* HTML */ \`\\u{prettier}\\u{0065}\`;
+foo = /* GraphQL */ \`\\u{prettier}\\u{0065}\`;
+
+foo = foo\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = html\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = graphql\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = markdown\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = css\`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = /* HTML */ \`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+foo = /* GraphQL */ \`\\u{prettier}\${foo}pr\\u{0065}ttier\`;
+
+foo = foo\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = html\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = graphql\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = markdown\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = css\`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = /* HTML */ \`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+foo = /* GraphQL */ \`pr\\u{0065}ttier\${foo}\\u{prettier}\`;
+
+foo = foo\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = html\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = graphql\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = markdown\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = css\`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = /* HTML */ \`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+foo = /* GraphQL */ \`pr\\u{0065}ttier\${foo}\\u{prettier}\${bar}pr\\u{0065}ttier\`;
+
+================================================================================
+`;
diff --git a/tests/js/multiparser-invalid/jsfmt.spec.js b/tests/js/multiparser-invalid/jsfmt.spec.js
new file mode 100644
index 000000000000..6e2b8e1a6b99
--- /dev/null
+++ b/tests/js/multiparser-invalid/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname, ["babel", "flow", "typescript"], {
+  errors: { espree: true, flow: true, typescript: true },
+});
diff --git a/tests/js/multiparser-invalid/text.js b/tests/js/multiparser-invalid/text.js
new file mode 100644
index 000000000000..37a612ed536d
--- /dev/null
+++ b/tests/js/multiparser-invalid/text.js
@@ -0,0 +1,31 @@
+foo = foo`\u{prettier}\u{0065}`;
+foo = html`\u{prettier}\u{0065}`;
+foo = graphql`\u{prettier}\u{0065}`;
+foo = markdown`\u{prettier}\u{0065}`;
+foo = css`\u{prettier}\u{0065}`;
+foo = /* HTML */`\u{prettier}\u{0065}`;
+foo = /* GraphQL */`\u{prettier}\u{0065}`;
+
+foo = foo`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = html`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = graphql`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = markdown`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = css`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = /* HTML */`\u{prettier}${foo}pr\u{0065}ttier`;
+foo = /* GraphQL */`\u{prettier}${foo}pr\u{0065}ttier`;
+
+foo = foo`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = html`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = graphql`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = markdown`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = css`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = /* HTML */`pr\u{0065}ttier${foo}\u{prettier}`;
+foo = /* GraphQL */`pr\u{0065}ttier${foo}\u{prettier}`;
+
+foo = foo`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = html`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = graphql`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = markdown`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = css`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = /* HTML */`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;
+foo = /* GraphQL */`pr\u{0065}ttier${foo}\u{prettier}${bar}pr\u{0065}ttier`;