Repository: prettier/prettier (JavaScript)
Date: 2020-06-10 01:59:20+00:00
Task type: Bug Fix

Issue:
Directive followed by comment has extra trailing hardline
If your file is only a directive, followed by a new line, followed by a comment, prettier ends the file with two newlines, rather than a single one. This is reproducible with only the babylon parser.

**Prettier 1.10.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEByArgZzgAkzAJwEswZUBuAHSmoHpadIBbJhGakAGhAgAcYi0TMlC9isAOpEAJjAAWyABwAGbjACGAIymyFSAEzdMRKAHMANnACK6CPGQAzdeexqC6ouZOmAwhBbqyCBQ0HBcIJruYADWcDAAyrzqYN7IhOhw3ABWmAAeAEJRsQnqrAAyJmFITi6ZIEkE2ARBmloAnubQ4dhMRGkEGdxYcAAqWsLVzq4gJk0wAArupkyBk7XcBHAAjuhEm4vqy6s102IQ2BLuvEHh6gQEEADu83cIEyDh0hBgjlN16vhfuseOgYLxQfogdgAL7QoA)
```sh
--arrow-parens 
--prose-wrap 
```

**Input:**
```jsx
'use strict';

// comment

```

**Output:**
```jsx
"use strict";

// comment


```

**Expected behavior:**

Code:
```jsx
'use strict';

// comment
```

![image](https://user-images.githubusercontent.com/1612134/26990368-503f5214-4d24-11e7-8093-d4edabec9830.png)

<details>
<summary>Old details</summary>
I believe the hardline is (in part) coming from 
https://github.com/prettier/prettier/blob/master/src/printer.js#L199

Though I partially wonder if `addAlignmentSize` [trim logic](https://github.com/prettier/prettier/blob/master/index.js#L85) could be expanded for the common case?
</details>


Issue discussion:

Solution patch:
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
index 68875559ab90..cf4a211c78c4 100644
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -449,12 +449,17 @@ function printPathNoParens(path, options, print, args) {
 
       return concat(parts);
 
-    case "Program":
+    case "Program": {
+      const hasContents =
+        !n.body.every(({ type }) => type === "EmptyStatement") || n.comments;
+
       // Babel 6
       if (n.directives) {
-        path.each((childPath) => {
+        const directivesCount = n.directives.length;
+        path.map((childPath, index) => {
           parts.push(print(childPath), semi, hardline);
           if (
+            (index < directivesCount - 1 || hasContents) &&
             isNextLineEmpty(
               options.originalText,
               childPath.getValue(),
@@ -477,14 +482,12 @@ function printPathNoParens(path, options, print, args) {
       );
 
       // Only force a trailing newline if there were any contents.
-      if (
-        !n.body.every(({ type }) => type === "EmptyStatement") ||
-        n.comments
-      ) {
+      if (hasContents) {
         parts.push(hardline);
       }
 
       return concat(parts);
+    }
     // Babel extension.
     case "EmptyStatement":
       return "";


Test patch:
diff --git a/tests/js/directives/jsfmt.spec.js b/tests/js/directives/jsfmt.spec.js
index eb85eda6bd02..f5b37cd79521 100644
--- a/tests/js/directives/jsfmt.spec.js
+++ b/tests/js/directives/jsfmt.spec.js
@@ -1,1 +1,48 @@
-run_spec(__dirname, ["babel", "flow", "typescript"]);
+const { outdent } = require("outdent");
+
+run_spec(
+  {
+    dirname: __dirname,
+    snippets: [
+      {
+        code: outdent`
+          'use strict';
+
+          // comment
+        `,
+        output:
+          outdent`
+            "use strict";
+
+            // comment
+          ` + "\n",
+      },
+      {
+        code: outdent`
+          'use strict';
+          // comment
+        `,
+        output:
+          outdent`
+            "use strict";
+            // comment
+          ` + "\n",
+      },
+      {
+        code:
+          outdent`
+            'use strict';
+
+            // comment
+          ` + "\n",
+        output:
+          outdent`
+            "use strict";
+
+            // comment
+          ` + "\n",
+      },
+    ],
+  },
+  ["babel", "flow", "typescript"]
+);