Repository: prettier/prettier (JavaScript)
Date: 2018-11-28 14:24:29+00:00
Task type: Bug Fix

Issue:
getFileInfo does not resolve ignores correctly when filePath is absolute
I received the following issue on eslint-plugin-prettier: https://github.com/prettier/eslint-plugin-prettier/issues/126

When passing an absolute path into `getFileInfo()` (or in my case `getFileInfo.sync()`) the line `src/not_pretty.js` within a `.prettierignore` is not honored. This results in a file being parsed by prettier when it should be ignored.

This seems to occur for any ignore line that contains a path instead of a single segment.

**Environments:**
- Prettier Version: 1.15.2
- Running Prettier via: vis eslint-plugin-prettier
- Runtime:Node 10
- Operating System: macOS

**Steps to reproduce:**

`git clone https://github.com/edmorley/testcase-eslint-plugin-prettier-ignore` and cd into the folder

Add the following file to the repository as `check-getfileinfo.js` and run it using `node check-getfileinfo.js`:

```js
const path = require("path");
const prettier = require("prettier");

filePath = "src/not_pretty.js";
options = { ignorePath: ".prettierignore" };

const relativeInfo = prettier.getFileInfo.sync(filePath, options);
const relativeWithDotInfo = prettier.getFileInfo.sync(`./${filePath}`, options);
const absoluteInfo = prettier.getFileInfo.sync(path.resolve(filePath), options);

console.log("relativePath is Ignored:", relativeInfo.ignored);
console.log("relativeWithDot is Ignored:", relativeWithDotInfo.ignored);
console.log("absolutePath is Ignored:", absoluteInfo.ignored);
```


**Expected behavior:**

All console logs should return true

**Actual behavior:**

- When using a relative path the ignored value is `true`
- When using a relative path with a leading . the ignored value is `false`
- When using an absolute path the ignored value is `false`

---

This behaviour does not occur when using the prettier CLI as all paths get resolved to be relative due to #2969. I think the solution is ensure the paths are relative when using getFileInfo() too.


Issue discussion:
We should `path.relative(ignorePath, filePath)` before passing into `ignorer.ignores(filePath)` if it's an absolute path.
One other test case to consider: the filePath`./src/not_pretty.js` should also be ignored. I've edited my above test script to cover that.

I think if we only test for absolute paths then that case won't be covered.

Solution patch:
diff --git a/src/common/get-file-info.js b/src/common/get-file-info.js
index 88bade3767df..a587b31ee5e9 100644
--- a/src/common/get-file-info.js
+++ b/src/common/get-file-info.js
@@ -2,6 +2,7 @@
 
 const createIgnorer = require("./create-ignorer");
 const options = require("../main/options");
+const path = require("path");
 
 /**
  * @typedef {{ ignorePath?: string, withNodeModules?: boolean, plugins: object }} FileInfoOptions
@@ -19,7 +20,11 @@ const options = require("../main/options");
  */
 function getFileInfo(filePath, opts) {
   return createIgnorer(opts.ignorePath, opts.withNodeModules).then(ignorer =>
-    _getFileInfo(ignorer, filePath, opts.plugins)
+    _getFileInfo(
+      ignorer,
+      normalizeFilePath(filePath, opts.ignorePath),
+      opts.plugins
+    )
   );
 }
 
@@ -30,7 +35,11 @@ function getFileInfo(filePath, opts) {
  */
 getFileInfo.sync = function(filePath, opts) {
   const ignorer = createIgnorer.sync(opts.ignorePath, opts.withNodeModules);
-  return _getFileInfo(ignorer, filePath, opts.plugins);
+  return _getFileInfo(
+    ignorer,
+    normalizeFilePath(filePath, opts.ignorePath),
+    opts.plugins
+  );
 };
 
 function _getFileInfo(ignorer, filePath, plugins) {
@@ -43,4 +52,10 @@ function _getFileInfo(ignorer, filePath, plugins) {
   };
 }
 
+function normalizeFilePath(filePath, ignorePath) {
+  return ignorePath
+    ? path.relative(path.dirname(ignorePath), filePath)
+    : filePath;
+}
+
 module.exports = getFileInfo;


Test patch:
diff --git a/tests_integration/__tests__/file-info.js b/tests_integration/__tests__/file-info.js
index 085db27c4eba..3d2710492233 100644
--- a/tests_integration/__tests__/file-info.js
+++ b/tests_integration/__tests__/file-info.js
@@ -1,6 +1,8 @@
 "use strict";
 
 const path = require("path");
+const tempy = require("tempy");
+const fs = require("fs");
 
 const runPrettier = require("../runPrettier");
 const prettier = require("prettier/local");
@@ -176,6 +178,40 @@ test("API getFileInfo.sync with ignorePath", () => {
   });
 });
 
+describe("API getFileInfo.sync with ignorePath", () => {
+  let cwd;
+  let filePath;
+  let options;
+  beforeAll(() => {
+    cwd = process.cwd();
+    const tempDir = tempy.directory();
+    process.chdir(tempDir);
+    const fileDir = "src";
+    filePath = `${fileDir}/should-be-ignored.js`;
+    const ignorePath = path.join(tempDir, ".prettierignore");
+    fs.writeFileSync(ignorePath, filePath, "utf8");
+    options = { ignorePath };
+  });
+  afterAll(() => {
+    process.chdir(cwd);
+  });
+  test("with relative filePath", () => {
+    expect(
+      prettier.getFileInfo.sync(filePath, options).ignored
+    ).toMatchInlineSnapshot(`true`);
+  });
+  test("with relative filePath starts with dot", () => {
+    expect(
+      prettier.getFileInfo.sync(`./${filePath}`, options).ignored
+    ).toMatchInlineSnapshot(`true`);
+  });
+  test("with absolute filePath", () => {
+    expect(
+      prettier.getFileInfo.sync(path.resolve(filePath), options).ignored
+    ).toMatchInlineSnapshot(`true`);
+  });
+});
+
 test("API getFileInfo with withNodeModules", () => {
   const file = path.resolve(
     path.join(__dirname, "../cli/with-node-modules/node_modules/file.js")