Repository: prettier/prettier (JavaScript)
Date: 2017-02-11 19:41:34+00:00
Task type: Bug Fix

Issue:
Ternary comments are not stable
```js
var inspect = (4 === util.inspect.length ?
  // node <= 0.8.x
  function (v, colors) {
    return util.inspect(v, void 0, void 0, colors);
  } :
  // node > 0.8.x
  function (v, colors) {
    return util.inspect(v, { colors: colors });
  }
);
```

turns into

```js
var inspect = 4 === util.inspect.length
  ? // node <= 0.8.x
    (function(v, colors) {
      return util.inspect(v, void 0, void 0, colors);
    })
  : // node > 0.8.x
    (function(v, colors) {
      return util.inspect(v, { colors: colors });
    });
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22var%20inspect%20%3D%20(4%20%3D%3D%3D%20util.inspect.length%20%3F%5Cn%20%20%2F%2F%20node%20%3C%3D%200.8.x%5Cn%20%20function%20(v%2C%20colors)%20%7B%5Cn%20%20%20%20return%20util.inspect(v%2C%20void%200%2C%20void%200%2C%20colors)%3B%5Cn%20%20%7D%20%3A%5Cn%20%20%2F%2F%20node%20%3E%200.8.x%5Cn%20%20function%20(v%2C%20colors)%20%7B%5Cn%20%20%20%20return%20util.inspect(v%2C%20%7B%20colors%3A%20colors%20%7D)%3B%5Cn%20%20%7D%5Cn)%3B%5Cn%5Cn%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D

which then turns into

```js
var inspect = 4 === util.inspect.length // node <= 0.8.x
  ? (function(v, colors) {
      return util.inspect(v, void 0, void 0, colors);
    }) // node > 0.8.x
  : (function(v, colors) {
      return util.inspect(v, { colors: colors });
    });
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22var%20inspect%20%3D%204%20%3D%3D%3D%20util.inspect.length%5Cn%20%20%3F%20%2F%2F%20node%20%3C%3D%200.8.x%5Cn%20%20%20%20(function(v%2C%20colors)%20%7B%5Cn%20%20%20%20%20%20return%20util.inspect(v%2C%20void%200%2C%20void%200%2C%20colors)%3B%5Cn%20%20%20%20%7D)%5Cn%20%20%3A%20%2F%2F%20node%20%3E%200.8.x%5Cn%20%20%20%20(function(v%2C%20colors)%20%7B%5Cn%20%20%20%20%20%20return%20util.inspect(v%2C%20%7B%20colors%3A%20colors%20%7D)%3B%5Cn%20%20%20%20%7D)%3B%5Cn%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D


Issue discussion:
Another case:

```js
const extractTextPluginOptions = shouldUseRelativeAssetPaths
  // Making sure that the publicPath goes back to to build folder.
  ? { publicPath: Array(cssFilename.split('/').length).join('../') } :
  {};
```

becomes

```js
const extractTextPluginOptions = shouldUseRelativeAssetPaths
  ? // Making sure that the publicPath goes back to to build folder.
    { publicPath: Array(cssFilename.split("/").length).join("../") }
  : {};
```

and then 

```js
const extractTextPluginOptions = shouldUseRelativeAssetPaths // Making sure that the publicPath goes back to to build folder.
  ? { publicPath: Array(cssFilename.split("/").length).join("../") }
  : {};
```

https://jlongster.github.io/prettier/#%7B%22content%22%3A%22const%20extractTextPluginOptions%20%3D%20shouldUseRelativeAssetPaths%5Cn%20%20%2F%2F%20Making%20sure%20that%20the%20publicPath%20goes%20back%20to%20to%20build%20folder.%5Cn%20%20%3F%20%7B%20publicPath%3A%20Array(cssFilename.split('%2F').length).join('..%2F')%20%7D%20%3A%5Cn%20%20%7B%7D%3B%22%2C%22options%22%3A%7B%22printWidth%22%3A80%2C%22tabWidth%22%3A2%2C%22singleQuote%22%3Afalse%2C%22trailingComma%22%3Afalse%2C%22bracketSpacing%22%3Atrue%2C%22doc%22%3Afalse%7D%7D

Solution patch:
diff --git a/src/comments.js b/src/comments.js
index d97dae762510..94b093057690 100644
--- a/src/comments.js
+++ b/src/comments.js
@@ -157,9 +157,11 @@ function attach(comments, ast, text) {
         addDanglingComment(ast, comment);
       }
     } else if (util.hasNewline(text, locEnd(comment))) {
-      // There is content before this comment on the same line, but
-      // none after it, so prefer a trailing comment of the previous node.
-      if (precedingNode) {
+      if (handleConditionalExpressionComments(enclosingNode, followingNode, comment)) {
+        // We're good
+      } else if (precedingNode) {
+        // There is content before this comment on the same line, but
+        // none after it, so prefer a trailing comment of the previous node.
         addTrailingComment(precedingNode, comment);
       } else if (followingNode) {
         addLeadingComment(followingNode, comment);
@@ -367,6 +369,14 @@ function handleMemberExpressionComment(enclosingNode, followingNode, comment) {
   return false;
 }
 
+function handleConditionalExpressionComments(enclosingNode, followingNode, comment) {
+  if (enclosingNode && enclosingNode.type === 'ConditionalExpression' && followingNode) {
+    addLeadingComment(followingNode, comment);
+    return true;
+  }
+  return false;
+}
+
 function printComment(commentPath) {
   const comment = commentPath.getValue();
 


Test patch:
diff --git a/tests/conditional/__snapshots__/jsfmt.spec.js.snap b/tests/conditional/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..688b109568d5
--- /dev/null
+++ b/tests/conditional/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,71 @@
+exports[`test comments.js 1`] = `
+"var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  // Making sure that the publicPath goes back to to build folder.
+  ? { publicPath: Array(cssFilename.split(\'/\').length).join(\'../\') } :
+  {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  ? // Making sure that the publicPath goes back to to build folder.
+    { publicPath: Array(cssFilename.split(\"/\").length).join(\"../\") }
+  : {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths // Making sure that the publicPath goes back to to build folder.
+  ? { publicPath: Array(cssFilename.split(\"/\").length).join(\"../\") }
+  : {};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  ? // Making sure that the publicPath goes back to to build folder.
+    { publicPath: Array(cssFilename.split(\"/\").length).join(\"../\") }
+  : {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  ? // Making sure that the publicPath goes back to to build folder.
+    { publicPath: Array(cssFilename.split(\"/\").length).join(\"../\") }
+  : {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  ? // Making sure that the publicPath goes back to to build folder.
+    { publicPath: Array(cssFilename.split(\"/\").length).join(\"../\") }
+  : {};
+"
+`;
diff --git a/tests/conditional/comments.js b/tests/conditional/comments.js
new file mode 100644
index 000000000000..78e9a024f609
--- /dev/null
+++ b/tests/conditional/comments.js
@@ -0,0 +1,33 @@
+var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+var inspect = 4 === util.inspect.length
+  ? // node <= 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, void 0, void 0, colors);
+    })
+  : // node > 0.8.x
+    (function(v, colors) {
+      return util.inspect(v, { colors: colors });
+    });
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  // Making sure that the publicPath goes back to to build folder.
+  ? { publicPath: Array(cssFilename.split('/').length).join('../') } :
+  {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths
+  ? // Making sure that the publicPath goes back to to build folder.
+    { publicPath: Array(cssFilename.split("/").length).join("../") }
+  : {};
+
+const extractTextPluginOptions = shouldUseRelativeAssetPaths // Making sure that the publicPath goes back to to build folder.
+  ? { publicPath: Array(cssFilename.split("/").length).join("../") }
+  : {};
diff --git a/tests/conditional/jsfmt.spec.js b/tests/conditional/jsfmt.spec.js
new file mode 100644
index 000000000000..989047bccc52
--- /dev/null
+++ b/tests/conditional/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);