Repository: prettier/prettier (JavaScript)
Date: 2017-02-20 16:41:58+00:00
Task type: Bug Fix

Issue:
End of line comment in template literal turns into invalid
```js
`
${// $FlowFixMe found when converting React.createClass to ES6
ExampleStory.getFragment('story')}
`
```

turns into

```js
`
${ExampleStory.getFragment('story')} // $FlowFixMe found when converting React.createClass to ES6
`;
```

which changes the meaning since the comment is now part of the text of the template literal.

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22%60%5Cn%24%7B%2F%2F%20%24FlowFixMe%20found%20when%20converting%20React.createClass%20to%20ES6%5CnExampleStory.getFragment('story')%7D%5Cn%60%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Atrue%2C%22trailingComma%22%3Atrue%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D


Issue discussion:
@vjeux How do you want the comment to be inserted then? As a leading one, completely before the template literal, or still inside the`${...}`? 
I don't really know, the way it was initially printed seems fine.
This looks ok to me too. I pushed a minor commit in order to deal with trailing comments inside the template literal.

Solution patch:
diff --git a/src/doc-builders.js b/src/doc-builders.js
index 9402f9da08c2..6c1ec5555981 100644
--- a/src/doc-builders.js
+++ b/src/doc-builders.js
@@ -67,6 +67,7 @@ function lineSuffix(contents) {
   return { type: "line-suffix", contents };
 }
 
+const lineSuffixBoundary = { type: "line-suffix-boundary" };
 const breakParent = { type: "break-parent" };
 const line = { type: "line" };
 const softline = { type: "line", soft: true };
@@ -100,6 +101,7 @@ module.exports = {
   group,
   conditionalGroup,
   lineSuffix,
+  lineSuffixBoundary,
   breakParent,
   ifBreak,
   indent
diff --git a/src/doc-printer.js b/src/doc-printer.js
index e41ba094160d..82b493026d3e 100644
--- a/src/doc-printer.js
+++ b/src/doc-printer.js
@@ -192,6 +192,11 @@ function printDocToString(doc, width, newLine) {
         case "line-suffix":
           lineSuffix.push([ind, mode, doc.contents]);
           break;
+        case "line-suffix-boundary":
+          if (lineSuffix.length > 0) {
+            cmds.push([ind, mode, { type: "line", hard: true }]);
+          }
+          break;
         case "line":
           switch (mode) {
             // fallthrough
diff --git a/src/printer.js b/src/printer.js
index ee76ce91fff0..b86ce359cd67 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -18,6 +18,7 @@ var indent = docBuilders.indent;
 var conditionalGroup = docBuilders.conditionalGroup;
 var ifBreak = docBuilders.ifBreak;
 var breakParent = docBuilders.breakParent;
+var lineSuffixBoundary = docBuilders.lineSuffixBoundary;
 
 var docUtils = require("./doc-utils");
 var willBreak = docUtils.willBreak;
@@ -1230,7 +1231,7 @@ function genericPrintNoParens(path, options, print) {
             n.expression.type === "LogicalExpression");
 
       if (shouldInline) {
-        return group(concat(["{", path.call(print, "expression"), "}"]));
+        return group(concat(["{", path.call(print, "expression"), lineSuffixBoundary, "}"]));
       }
 
       return group(
@@ -1241,6 +1242,7 @@ function genericPrintNoParens(path, options, print) {
             concat([softline, path.call(print, "expression")])
           ),
           softline,
+          lineSuffixBoundary,
           "}"
         ])
       );
@@ -1387,7 +1389,11 @@ function genericPrintNoParens(path, options, print) {
           parts.push(print(childPath));
 
           if (i < expressions.length) {
-            parts.push("${", removeLines(expressions[i]), "}");
+            parts.push(
+              "${",
+              removeLines(expressions[i]), lineSuffixBoundary,
+              "}"
+            );
           }
         },
         "quasis"


Test patch:
diff --git a/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap b/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..e4fc91e727b2
--- /dev/null
+++ b/tests/line_suffix_boundary/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,103 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`boundary.js 1`] = `
+"\`\${
+a +  // a
+  a
+}
+
+\${a // comment
+}
+
+\${b /* comment */}
+
+\${/* comment */ c /* comment */}
+
+\${// comment
+d //comment
+}
+
+\${// $FlowFixMe found when converting React.createClass to ES6
+ExampleStory.getFragment('story')}
+\`;
+
+<div>
+{ExampleStory.getFragment('story') // $FlowFixMe found when converting React.createClass to ES6
+}
+</div>;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\`\${a + a // a
+}
+
+\${/* comment*/
+a}
+
+\${/* comment */ b}
+
+\${/* comment */ /* comment */ c}
+
+\${/* comment*/
+/*comment*/
+d}
+
+\${/* $FlowFixMe found when converting React.createClass to ES6*/
+ExampleStory.getFragment(\\"story\\")}
+\`;
+
+<div>
+  {ExampleStory.getFragment(\\"story\\") // $FlowFixMe found when converting React.createClass to ES6
+  }
+</div>;
+"
+`;
+
+exports[`boundary.js 2`] = `
+"\`\${
+a +  // a
+  a
+}
+
+\${a // comment
+}
+
+\${b /* comment */}
+
+\${/* comment */ c /* comment */}
+
+\${// comment
+d //comment
+}
+
+\${// $FlowFixMe found when converting React.createClass to ES6
+ExampleStory.getFragment('story')}
+\`;
+
+<div>
+{ExampleStory.getFragment('story') // $FlowFixMe found when converting React.createClass to ES6
+}
+</div>;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\`\${a + a // a
+}
+
+\${/* comment*/
+a}
+
+\${/* comment */ b}
+
+\${/* comment */ /* comment */ c}
+
+\${/* comment*/
+/*comment*/
+d}
+
+\${/* $FlowFixMe found when converting React.createClass to ES6*/
+ExampleStory.getFragment(\\"story\\")}
+\`;
+
+<div>
+  {ExampleStory.getFragment(\\"story\\") // $FlowFixMe found when converting React.createClass to ES6
+  }
+</div>;
+"
+`;
diff --git a/tests/line_suffix_boundary/boundary.js b/tests/line_suffix_boundary/boundary.js
new file mode 100644
index 000000000000..e108933d97b4
--- /dev/null
+++ b/tests/line_suffix_boundary/boundary.js
@@ -0,0 +1,24 @@
+`${
+a +  // a
+  a
+}
+
+${a // comment
+}
+
+${b /* comment */}
+
+${/* comment */ c /* comment */}
+
+${// comment
+d //comment
+}
+
+${// $FlowFixMe found when converting React.createClass to ES6
+ExampleStory.getFragment('story')}
+`;
+
+<div>
+{ExampleStory.getFragment('story') // $FlowFixMe found when converting React.createClass to ES6
+}
+</div>;
diff --git a/tests/line_suffix_boundary/jsfmt.spec.js b/tests/line_suffix_boundary/jsfmt.spec.js
new file mode 100644
index 000000000000..d5ec770a90fe
--- /dev/null
+++ b/tests/line_suffix_boundary/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname);
+run_spec(__dirname, {parser: 'babylon'});