Repository: prettier/prettier (JavaScript)
Date: 2018-08-08 17:04:50+00:00
Task type: Bug Fix

Issue:
Yaml files with CRLF are formatted as if they use LF
<!--

BEFORE SUBMITTING AN ISSUE:

1.  Search for your issue on GitHub: https://github.com/prettier/prettier/issues
    A large number of opened issues are duplicates of existing issues.
    If someone has already opened an issue for what you are experiencing,
    you do not need to open a new issue — please add a 👍 reaction to the
    existing issue instead.

2.  We get a lot of requests for adding options, but Prettier is
    built on the principle of being opinionated about code formatting.
    This means we have a very high bar for adding new options.
    Find out more: https://prettier.io/docs/en/option-philosophy.html

Tip! Don't write this stuff manually.

1. Go to https://prettier.io/playground
2. Paste your code and set options
3. Press the "Report issue" button in the lower right

-->

**Prettier 1.14.0**
~[Playground link](#)~ – n/a as the issue is Windows-only

**Input:**
Any yaml with `CRLF` (a.k.a. `\r\n`), e.g. on a Windows laptop where git replaces `LF` with `CRLF` on checkout – see [config docs](https://help.github.com/articles/dealing-with-line-endings/#platform-windows)

**Output:**

The same yaml file, but with `LF` instead of `CRLF`

**Expected behavior:**

`CRLF` preserved, just like for files in other languages that are handled by Prettier

---

After the release of 1.14.0, I added yaml files to my project's `format` and `lint` npm scripts. All worked well on a macbook, just as it does for all other files that Prettier can deal with. However, after I executed `npm run lint` in a VSTS CI/CD pipeline and a colleague's Windows laptop, this script failed. As it turned out, Prettier replaced `CRLF` (Windows-flavoured line endings) with `LF`, which was not expected from it.

If I understand correctly, Prittier's AST printers are able to detect if the file is using `CRLF` or `LF` in the first place and preserve this choice in the output. This ended up being impossible for the yaml files because of an upstream bug (https://github.com/eemeli/yaml/issues/20), so a workaround was implemented (https://github.com/prettier/prettier/pull/4863). Here it is:

https://github.com/prettier/prettier/blob/1fe0b01bd4d547793a112718c6264bce78bb4c60/src/language-yaml/parser-yaml.js#L69-L72

Because of this transformation, Prittier's yaml printer is unable to recall the use of CRLF and produces a file with LF only. AppVeyor build passes because it seems to set `core.autocrlf=input` in it's`~/.gitconfig` (https://github.com/desktop/desktop/pull/5182#issuecomment-408877361).

Given that fixing the upstream bug may take some time, Is there any way to ‘teleport’ the knowledge about `CRLF` from `preprocess()` down to the printer? IMO this may work as a temporary solution.

Having a CI build that runs tests in  ‘`CRLF` mode’ would be also useful to avoid similar bugs in other languages in future.


Issue discussion:

Solution patch:
diff --git a/src/main/core.js b/src/main/core.js
index 47ef115f9cc5..b92e97cb6252 100644
--- a/src/main/core.js
+++ b/src/main/core.js
@@ -71,6 +71,8 @@ function coreFormat(text, opts, addAlignmentSize) {
 
   const parsed = parser.parse(text, opts);
   const ast = parsed.ast;
+
+  const originalText = text;
   text = parsed.text;
 
   if (opts.cursorOffset >= 0) {
@@ -82,7 +84,7 @@ function coreFormat(text, opts, addAlignmentSize) {
 
   const astComments = attachComments(text, ast, opts);
   const doc = printAstToDoc(ast, opts, addAlignmentSize);
-  opts.newLine = guessLineEnding(text);
+  opts.newLine = guessLineEnding(originalText);
 
   const result = printDocToString(doc, opts);
 


Test patch:
diff --git a/tests_integration/__tests__/__snapshots__/format.js.snap b/tests_integration/__tests__/__snapshots__/format.js.snap
index 164d9cb180aa..ce4f8aea5dca 100644
--- a/tests_integration/__tests__/__snapshots__/format.js.snap
+++ b/tests_integration/__tests__/__snapshots__/format.js.snap
@@ -9,7 +9,4 @@ exports[`typescript parser should throw the first error when both JSX and non-JS
   10 |   "
 `;
 
-exports[`yaml parser should handle CRLF correctly 1`] = `
-"a: 123
-"
-`;
+exports[`yaml parser should handle CRLF correctly 1`] = `"\\"a: 123\\\\r\\\\n\\""`;
diff --git a/tests_integration/__tests__/format.js b/tests_integration/__tests__/format.js
index bf730ae99b60..1320e5ba7ae3 100644
--- a/tests_integration/__tests__/format.js
+++ b/tests_integration/__tests__/format.js
@@ -4,7 +4,10 @@ const prettier = require("prettier/local");
 
 test("yaml parser should handle CRLF correctly", () => {
   const input = "a:\r\n  123\r\n";
-  expect(prettier.format(input, { parser: "yaml" })).toMatchSnapshot();
+  expect(
+    // use JSON.stringify to observe CRLF
+    JSON.stringify(prettier.format(input, { parser: "yaml" }))
+  ).toMatchSnapshot();
 });
 
 test("typescript parser should throw the first error when both JSX and non-JSX mode failed", () => {