Repository: prettier/prettier (JavaScript)

Issue:
Can't print comment after directive
**Prettier 2.8.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEByArgZzgAkzAJwEswZUBuAHSmoHoAqHSAW2YRh3tuoG0BdAHQAzCAQCiAQzAALABQBKciAA0ICAAcYRaJmSgJBAhADuABQMJdKCQBtjEgJ67VAIwJSA1nBgBlCWwAZIig4ZCFbbFd3MC9fdSlggHNkQnQ4VThmFzgAExzcgIkoRPQJRLgAMVFmCRgtYuQQCXQYCBUQaRhmGwB1aSJ4THiwOB9LAaIANwGHRrBMZxBg7AIYU3dEmrCI9JAAK0wADx8kmzgARXQIeG2bSJB4ghXGqVEodvViWB6iHJhpZAADgADKpPhBsD13OpGp84CtJqFVABHK7wdYaKxNTAAWhCuVy7QIcFRRGJ6zKWyQ4Tuu2wzCIt3umFOcAAgnViC4WnBTHACEEQky6azLtdQtSdqoYBIXD8-gCkAAmaXuIg2JIAYQgrAkjXhAFZ2lg4AAVWVWGn3SZpACSUHysB8YGImjZDp8MAcZ2FAF9fUA)
<!-- prettier-ignore -->
```sh
--parser acorn
--no-semi
```

**Input:**
<!-- prettier-ignore -->
```jsx
'use strict';

/* comment */
[].forEach();
```

**Output:**
<!-- prettier-ignore -->
```jsx
"use strict"

/* comment */
;[].forEach()

```

**Second Output:**
<!-- prettier-ignore -->
```jsx
Error: Comment "comment" was not printed. Please report this error!
    at Object.N [as ensureAllCommentsPrinted] (https://prettier.io/lib/standalone.js:41:7554)
    at A (https://prettier.io/lib/standalone.js:41:15193)
    at x (https://prettier.io/lib/standalone.js:45:576)
    at Object.formatWithCursor (https://prettier.io/lib/standalone.js:116:7283)
    at formatCode (https://prettier.io/worker.js:173:21)
    at handleFormatMessage (https://prettier.io/worker.js:161:34)
    at handleMessage (https://prettier.io/worker.js:77:14)
    at self.onmessage (https://prettier.io/worker.js:53:14)
```

**Expected behavior:**

Should be able to print

---

Reproduce in semi mode

**Prettier 2.8.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEByArgZzgAkzAJwEswZUcB6AKh0gFs6EYcqKBuEAGhAgAcYi0TMlABDAgQgB3AAriEwlKIA2U0QE9h3AEYFRYANZwYAZVGMAMkShxkAMxXYdew8ZO991gObJC6ONxwdNpwACahYRaiUF7ool5wAGIQBHSiMAIxyCCi6DAQXCAAFjB0ygDqRUTwmB5gcCYK1UQAbtXq2WCYWiDW2AQwMnpeafaOASAAVpgAHibeynAAiugQ8GPKTiAeBP3Z+ilQhbzEsOVEoTBFyAAcAAzcJxDY5Xq82Sdw-S223ACOq3gQz4ihymAAtDYwmFCgQ4ACiHChvFRkgHJsJtg6ERfAR-NxMAs4ABBDLEbR5OAyOAEKw2DZbQkxRYrNa2NHjbgwUTac6Xa5IABMXL0RGU3gAwhAGKJsl8AKyFLBwAAqPMU6K2LX8AEkoBFYCYwMR+MT9SYYOpFgy4ABfW1AA)
<!-- prettier-ignore -->
```sh
--parser acorn
```

**Input:**
<!-- prettier-ignore -->
```jsx
'use strict' /* comment */;
```

**Output:**
<!-- prettier-ignore -->
```jsx
Error: Comment "comment" was not printed. Please report this error!
    at Object.N [as ensureAllCommentsPrinted] (https://prettier.io/lib/standalone.js:41:7554)
    at A (https://prettier.io/lib/standalone.js:41:15193)
    at x (https://prettier.io/lib/standalone.js:45:576)
    at Object.formatWithCursor (https://prettier.io/lib/standalone.js:116:7283)
    at formatCode (https://prettier.io/worker.js:173:21)
    at handleFormatMessage (https://prettier.io/worker.js:104:24)
    at handleMessage (https://prettier.io/worker.js:77:14)
    at self.onmessage (https://prettier.io/worker.js:53:14)
```



Hints:

Patch:
diff --git a/src/language-js/print/literal.js b/src/language-js/print/literal.js
--- a/src/language-js/print/literal.js
+++ b/src/language-js/print/literal.js
@@ -1,6 +1,7 @@
 "use strict";
 const { printString, printNumber } = require("../../common/util.js");
 const { replaceTextEndOfLine } = require("../../document/doc-utils.js");
+const { printDirective } = require("./misc.js");
 
 function printLiteral(path, options /*, print*/) {
   const node = path.getNode();
@@ -41,7 +42,9 @@ function printLiteral(path, options /*, print*/) {
       }
 
       if (typeof value === "string") {
-        return replaceTextEndOfLine(printString(node.raw, options));
+        return isDirective(path)
+          ? printDirective(node.raw, options)
+          : replaceTextEndOfLine(printString(node.raw, options));
       }
 
       return String(value);
@@ -49,6 +52,15 @@ function printLiteral(path, options /*, print*/) {
   }
 }
 
+function isDirective(path) {
+  if (path.getName() !== "expression") {
+    return;
+  }
+
+  const parent = path.getParentNode();
+  return parent.type === "ExpressionStatement" && parent.directive;
+}
+
 function printBigInt(raw) {
   return raw.toLowerCase();
 }
diff --git a/src/language-js/print/misc.js b/src/language-js/print/misc.js
--- a/src/language-js/print/misc.js
+++ b/src/language-js/print/misc.js
@@ -93,6 +93,24 @@ function printRestSpread(path, options, print) {
   return ["...", print("argument"), printTypeAnnotation(path, options, print)];
 }
 
+function printDirective(rawText, options) {
+  const rawContent = rawText.slice(1, -1);
+
+  // Check for the alternate quote, to determine if we're allowed to swap
+  // the quotes on a DirectiveLiteral.
+  if (rawContent.includes('"') || rawContent.includes("'")) {
+    return rawText;
+  }
+
+  const enclosingQuote = options.singleQuote ? "'" : '"';
+
+  // Directives are exact code unit sequences, which means that you can't
+  // change the escape sequences they use.
+  // See https://github.com/prettier/prettier/issues/1555
+  // and https://tc39.github.io/ecma262/#directive-prologue
+  return enclosingQuote + rawContent + enclosingQuote;
+}
+
 module.exports = {
   printOptionalToken,
   printDefiniteToken,
@@ -102,4 +120,5 @@ module.exports = {
   printTypeAnnotation,
   printRestSpread,
   adjustClause,
+  printDirective,
 };
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -23,7 +23,6 @@ const {
   isLineComment,
   isNextLineEmpty,
   needsHardlineAfterDanglingComment,
-  rawText,
   hasIgnoreComment,
   isCallExpression,
   isMemberExpression,
@@ -47,6 +46,7 @@ const {
   adjustClause,
   printRestSpread,
   printDefiniteToken,
+  printDirective,
 } = require("./print/misc.js");
 const {
   printImportDeclaration,
@@ -215,11 +215,6 @@ function printPathNoParens(path, options, print, args) {
     case "EmptyStatement":
       return "";
     case "ExpressionStatement": {
-      // Detect Flow and TypeScript directives
-      if (node.directive) {
-        return [printDirective(node.expression, options), semi];
-      }
-
       if (
         options.parser === "__vue_event_binding" ||
         options.parser === "__vue_ts_event_binding"
@@ -429,7 +424,7 @@ function printPathNoParens(path, options, print, args) {
     case "Directive":
       return [print("value"), semi]; // Babel 6
     case "DirectiveLiteral":
-      return printDirective(node, options);
+      return printDirective(node.extra.raw, options);
     case "UnaryExpression":
       parts.push(node.operator);
 
@@ -813,25 +808,6 @@ function printPathNoParens(path, options, print, args) {
   }
 }
 
-function printDirective(node, options) {
-  const raw = rawText(node);
-  const rawContent = raw.slice(1, -1);
-
-  // Check for the alternate quote, to determine if we're allowed to swap
-  // the quotes on a DirectiveLiteral.
-  if (rawContent.includes('"') || rawContent.includes("'")) {
-    return raw;
-  }
-
-  const enclosingQuote = options.singleQuote ? "'" : '"';
-
-  // Directives are exact code unit sequences, which means that you can't
-  // change the escape sequences they use.
-  // See https://github.com/prettier/prettier/issues/1555
-  // and https://tc39.github.io/ecma262/#directive-prologue
-  return enclosingQuote + rawContent + enclosingQuote;
-}
-
 function canAttachComment(node) {
   return (
     node.type &&


Test patch:
diff --git a/tests/format/js/directives/comments/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/directives/comments/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
--- /dev/null
+++ b/tests/format/js/directives/comments/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,569 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`snippet: #0 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+/* comment */ 'use strict';
+=====================================output=====================================
+/* comment */ "use strict"
+
+================================================================================
+`;
+
+exports[`snippet: #0 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+/* comment */ 'use strict';
+=====================================output=====================================
+/* comment */ "use strict";
+
+================================================================================
+`;
+
+exports[`snippet: #1 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  /* comment */ 'use strict';
+}
+=====================================output=====================================
+function foo() {
+  /* comment */ "use strict"
+}
+
+================================================================================
+`;
+
+exports[`snippet: #1 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  /* comment */ 'use strict';
+}
+=====================================output=====================================
+function foo() {
+  /* comment */ "use strict";
+}
+
+================================================================================
+`;
+
+exports[`snippet: #2 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+'use strict' /* comment */;
+=====================================output=====================================
+"use strict" /* comment */
+
+================================================================================
+`;
+
+exports[`snippet: #2 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+'use strict' /* comment */;
+=====================================output=====================================
+"use strict" /* comment */;
+
+================================================================================
+`;
+
+exports[`snippet: #3 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict' /* comment */;
+}
+=====================================output=====================================
+function foo() {
+  "use strict" /* comment */
+}
+
+================================================================================
+`;
+
+exports[`snippet: #3 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict' /* comment */;
+}
+=====================================output=====================================
+function foo() {
+  "use strict" /* comment */;
+}
+
+================================================================================
+`;
+
+exports[`snippet: #4 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+// comment
+'use strict';
+=====================================output=====================================
+// comment
+"use strict"
+
+================================================================================
+`;
+
+exports[`snippet: #4 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+// comment
+'use strict';
+=====================================output=====================================
+// comment
+"use strict";
+
+================================================================================
+`;
+
+exports[`snippet: #5 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  // comment
+  'use strict';
+}
+=====================================output=====================================
+function foo() {
+  // comment
+  "use strict"
+}
+
+================================================================================
+`;
+
+exports[`snippet: #5 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  // comment
+  'use strict';
+}
+=====================================output=====================================
+function foo() {
+  // comment
+  "use strict";
+}
+
+================================================================================
+`;
+
+exports[`snippet: #6 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+'use strict' // comment
+=====================================output=====================================
+"use strict" // comment
+
+================================================================================
+`;
+
+exports[`snippet: #6 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+'use strict' // comment
+=====================================output=====================================
+"use strict"; // comment
+
+================================================================================
+`;
+
+exports[`snippet: #7 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict' // comment
+}
+=====================================output=====================================
+function foo() {
+  "use strict" // comment
+}
+
+================================================================================
+`;
+
+exports[`snippet: #7 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict' // comment
+}
+=====================================output=====================================
+function foo() {
+  "use strict"; // comment
+}
+
+================================================================================
+`;
+
+exports[`snippet: #8 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+'use strict';
+/* comment */
+(function () {})();
+=====================================output=====================================
+"use strict"
+/* comment */
+;(function () {})()
+
+================================================================================
+`;
+
+exports[`snippet: #8 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+'use strict';
+/* comment */
+(function () {})();
+=====================================output=====================================
+"use strict";
+/* comment */
+(function () {})();
+
+================================================================================
+`;
+
+exports[`snippet: #9 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict';
+  /* comment */
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  "use strict"
+  /* comment */
+  ;(function () {})()
+}
+
+================================================================================
+`;
+
+exports[`snippet: #9 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict';
+  /* comment */
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  "use strict";
+  /* comment */
+  (function () {})();
+}
+
+================================================================================
+`;
+
+exports[`snippet: #10 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+/* comment */
+'use strict';
+(function () {})();
+=====================================output=====================================
+/* comment */
+"use strict"
+;(function () {})()
+
+================================================================================
+`;
+
+exports[`snippet: #10 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+/* comment */
+'use strict';
+(function () {})();
+=====================================output=====================================
+/* comment */
+"use strict";
+(function () {})();
+
+================================================================================
+`;
+
+exports[`snippet: #11 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  /* comment */
+  'use strict';
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  /* comment */
+  "use strict"
+  ;(function () {})()
+}
+
+================================================================================
+`;
+
+exports[`snippet: #11 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  /* comment */
+  'use strict';
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  /* comment */
+  "use strict";
+  (function () {})();
+}
+
+================================================================================
+`;
+
+exports[`snippet: #12 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+'use strict';
+// comment
+(function () {})();
+=====================================output=====================================
+"use strict"
+// comment
+;(function () {})()
+
+================================================================================
+`;
+
+exports[`snippet: #12 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+'use strict';
+// comment
+(function () {})();
+=====================================output=====================================
+"use strict";
+// comment
+(function () {})();
+
+================================================================================
+`;
+
+exports[`snippet: #13 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict';
+  // comment
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  "use strict"
+  // comment
+  ;(function () {})()
+}
+
+================================================================================
+`;
+
+exports[`snippet: #13 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  'use strict';
+  // comment
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  "use strict";
+  // comment
+  (function () {})();
+}
+
+================================================================================
+`;
+
+exports[`snippet: #14 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+// comment
+'use strict';
+(function () {})();
+=====================================output=====================================
+// comment
+"use strict"
+;(function () {})()
+
+================================================================================
+`;
+
+exports[`snippet: #14 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+// comment
+'use strict';
+(function () {})();
+=====================================output=====================================
+// comment
+"use strict";
+(function () {})();
+
+================================================================================
+`;
+
+exports[`snippet: #15 - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+semi: false
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  // comment
+  'use strict';
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  // comment
+  "use strict"
+  ;(function () {})()
+}
+
+================================================================================
+`;
+
+exports[`snippet: #15 format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+function foo() {
+  // comment
+  'use strict';
+  (function () {})();
+}
+=====================================output=====================================
+function foo() {
+  // comment
+  "use strict";
+  (function () {})();
+}
+
+================================================================================
+`;
diff --git a/tests/format/js/directives/comments/jsfmt.spec.js b/tests/format/js/directives/comments/jsfmt.spec.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/directives/comments/jsfmt.spec.js
@@ -0,0 +1,55 @@
+const { outdent } = require("outdent");
+const indent = (text) =>
+  text
+    .split("\n")
+    .map((line) => (line ? `  ${line}` : line))
+    .join("\n");
+// TODO: Remove this when we drop support for Node.js v10
+// eslint-disable-next-line unicorn/prefer-spread
+const flat = (array) => [].concat(...array);
+
+const snippets = flat(
+  [
+    "/* comment */ 'use strict';",
+    "'use strict' /* comment */;",
+    outdent`
+      // comment
+      'use strict';
+    `,
+    outdent`
+      'use strict' // comment
+    `,
+    outdent`
+      'use strict';
+      /* comment */
+      (function () {})();
+    `,
+    outdent`
+      /* comment */
+      'use strict';
+      (function () {})();
+    `,
+    outdent`
+      'use strict';
+      // comment
+      (function () {})();
+    `,
+    outdent`
+      // comment
+      'use strict';
+      (function () {})();
+    `,
+  ].map((code) => [
+    code,
+    outdent`
+      function foo() {
+      ${indent(code)}
+      }
+    `,
+  ])
+);
+
+run_spec({ dirname: __dirname, snippets }, ["babel", "flow", "typescript"]);
+run_spec({ dirname: __dirname, snippets }, ["babel", "flow", "typescript"], {
+  semi: false,
+});
diff --git a/tests/format/markdown/auto-link/jsfmt.spec.js b/tests/format/markdown/auto-link/jsfmt.spec.js
--- a/tests/format/markdown/auto-link/jsfmt.spec.js
+++ b/tests/format/markdown/auto-link/jsfmt.spec.js
@@ -3,6 +3,7 @@
  * @param {Array<Array<T>>} array
  * @returns {Array<T>}
  */
+// TODO: Remove this when we drop support for Node.js v10
 // eslint-disable-next-line unicorn/prefer-spread
 const flat = (array) => [].concat(...array);