Repository: okonet/lint-staged
Pull request #534 (2018-11-06 14:55:18)

feat: add relative option to let lint-staged work with ng lint
In order to work with the new version of [angular-cli](https://github.com/angular/angular-cli) which is now using `--files` parameter to pass specific RELATIVE file paths to lint ([see here](https://angular.io/cli/lint)) I wasn't able to let my project working with lint-staged. I think I'm not the only one in this situation.

Signed-off-by: Benjamin Coenen <benjamin.coenen@corp.ovh.com>

codecov[bot]: # [Codecov](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=h1) Report
> Merging [#534](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=desc) into [master](https://codecov.io/gh/okonet/lint-staged/commit/5e165a3437695933cc136979aa6fae37b3744c38?src=pr&el=desc) will **increase** coverage by `0.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/okonet/lint-staged/pull/534/graphs/tree.svg?width=650&token=LSoEgsjk4O&height=150&src=pr)](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #534      +/-   ##
==========================================
+ Coverage      98%   98.02%   +0.01%     
==========================================
  Files          13       13              
  Lines         351      354       +3     
  Branches       44       45       +1     
==========================================
+ Hits          344      347       +3     
  Misses          7        7
```


| [Impacted Files](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/getConfig.js](https://codecov.io/gh/okonet/lint-staged/pull/534/diff?src=pr&el=tree#diff-c3JjL2dldENvbmZpZy5qcw==) | `100% <ø> (ø)` | :arrow_up: |
| [src/generateTasks.js](https://codecov.io/gh/okonet/lint-staged/pull/534/diff?src=pr&el=tree#diff-c3JjL2dlbmVyYXRlVGFza3MuanM=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=footer). Last update [5e165a3...04fe88e](https://codecov.io/gh/okonet/lint-staged/pull/534?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

okonet: I’m wondering if this should be the default behavior. Not sure why we went with absolute paths tbh. I’ll think about it...
bnjjj: @okonet why not but it will be a breaking change if it's the default behavior
okonet: Yeah that's okay. My main question is why we even work with absolute paths. Anyone? @sudo-suhas @luftywiranda13?
buszu: Great work! Thanks to using relative paths I can run a linter from docker.
okonet: Okay some there is no reply from other contributors, let’s make this a default behavior and release as a breaking change. I am not a fan of flags and would like to drop advanced config at some time point. Can you please update the PR accordingly and I’ll merge it. Thanks!
bnjjj: @okonet Ok so no option to let user keep the absolute path ? 
okonet: I don’t see the point of having it. Am I missing some use cases?
bnjjj: @okonet the PR is ready :) 
bnjjj: PR updated :) 

Patch:
diff --git a/README.md b/README.md
index a74917e..450c880 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,7 @@ Notice that the linting commands now are nested into the `linters` object. The f
 * `ignore` - `['**/docs/**/*.js']` - array of glob patterns to entirely ignore from any task.
 * `linters` — `Object` — keys (`String`) are glob patterns, values (`Array<String> | String`) are commands to execute.
 * `subTaskConcurrency` — `1` — Controls concurrency for processing chunks generated for each linter. This option is only applicable on Windows. Execution is **not** concurrent by default(see [#225](https://github.com/okonet/lint-staged/issues/225))
+* `relative` — `false` — if `true` it will give the relative path from your `package.json` directory to your linter arguments.
 
 ## Filtering files
 
@@ -287,6 +288,17 @@ The following is equivalent:
 }
 ```
 
+### Use ng lint with angular cli >= 7.0.0
+
+```json
+{
+  "linters": {
+    "*.ts": "ng lint myProjectName --files"
+  },
+  "relative": true
+}
+```
+
 ### Stylelint for CSS with defaults and for SCSS with SCSS syntax
 
 ```json
diff --git a/src/generateTasks.js b/src/generateTasks.js
index 14bd319..bd120aa 100644
--- a/src/generateTasks.js
+++ b/src/generateTasks.js
@@ -33,9 +33,14 @@ module.exports = function generateTasks(config, stagedRelFiles) {
         .map(file => path.relative(cwd, file)),
       patterns,
       globOptions
-    )
+    ).map(file => {
+      // if you set relative option, then the file path will be relative to your package.json
+      if (config.relative) {
+        return path.normalize(file)
+      }
       // Return absolute path after the filter is run
-      .map(file => path.resolve(cwd, file))
+      return path.resolve(cwd, file)
+    })
 
     const task = { pattern, commands, fileList }
     debug('Generated task: \n%O', task)
diff --git a/src/getConfig.js b/src/getConfig.js
index e8576a0..0c8d051 100644
--- a/src/getConfig.js
+++ b/src/getConfig.js
@@ -28,7 +28,8 @@ const defaultConfig = {
   linters: {},
   ignore: [],
   subTaskConcurrency: 1,
-  renderer: 'update'
+  renderer: 'update',
+  relative: false
 }
 
 /**


Test patch:
diff --git a/test/__snapshots__/getConfig.spec.js.snap b/test/__snapshots__/getConfig.spec.js.snap
index 9e6d960..6f31467 100644
--- a/test/__snapshots__/getConfig.spec.js.snap
+++ b/test/__snapshots__/getConfig.spec.js.snap
@@ -10,6 +10,7 @@ Object {
   },
   "ignore": Array [],
   "linters": Object {},
+  "relative": false,
   "renderer": "update",
   "subTaskConcurrency": 1,
 }
@@ -25,6 +26,7 @@ Object {
   },
   "ignore": Array [],
   "linters": Object {},
+  "relative": false,
   "renderer": "update",
   "subTaskConcurrency": 1,
 }
@@ -46,6 +48,7 @@ Object {
     ],
     ".*rc": "jsonlint",
   },
+  "relative": false,
   "renderer": "update",
   "subTaskConcurrency": 1,
 }
diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.js.snap
index dd16819..1c8dc38 100644
--- a/test/__snapshots__/index.spec.js.snap
+++ b/test/__snapshots__/index.spec.js.snap
@@ -15,7 +15,8 @@ LOG {
   },
   ignore: [],
   subTaskConcurrency: 1,
-  renderer: 'verbose'
+  renderer: 'verbose',
+  relative: false
 }"
 `;
 
@@ -34,7 +35,8 @@ LOG {
   },
   ignore: [],
   subTaskConcurrency: 1,
-  renderer: 'verbose'
+  renderer: 'verbose',
+  relative: false
 }"
 `;
 
@@ -55,7 +57,8 @@ LOG {
   },
   ignore: [],
   subTaskConcurrency: 1,
-  renderer: 'verbose'
+  renderer: 'verbose',
+  relative: false
 }"
 `;
 
diff --git a/test/generateTasks.spec.js b/test/generateTasks.spec.js
index eb426aa..2564e42 100644
--- a/test/generateTasks.spec.js
+++ b/test/generateTasks.spec.js
@@ -86,6 +86,21 @@ describe('generateTasks', () => {
     })
   })
 
+  it('should return relative paths', () => {
+    const [task] = generateTasks(
+      {
+        linters: {
+          '*': 'lint'
+        },
+        relative: true
+      },
+      files
+    )
+    task.fileList.forEach(file => {
+      expect(path.isAbsolute(file)).toBe(false)
+    })
+  })
+
   it('should not match non-children files', () => {
     const relPath = path.join(process.cwd(), '..')
     resolveGitDir.mockReturnValueOnce(relPath)
@@ -126,6 +141,22 @@ describe('generateTasks', () => {
     })
   })
 
+  it('should match pattern "*.js" and return relative path', () => {
+    const result = generateTasks(Object.assign({}, config, { relative: true }), files)
+    const linter = result.find(item => item.pattern === '*.js')
+    expect(linter).toEqual({
+      pattern: '*.js',
+      commands: 'root-js',
+      fileList: [
+        `test.js`,
+        `deeper/test.js`,
+        `deeper/test2.js`,
+        `even/deeper/test.js`,
+        `.hidden/test.js`
+      ].map(path.normalize)
+    })
+  })
+
   it('should match pattern "**/*.js"', () => {
     const result = generateTasks(config, files)
     const linter = result.find(item => item.pattern === '**/*.js')
@@ -142,6 +173,22 @@ describe('generateTasks', () => {
     })
   })
 
+  it('should match pattern "**/*.js" with relative path', () => {
+    const result = generateTasks(Object.assign({}, config, { relative: true }), files)
+    const linter = result.find(item => item.pattern === '**/*.js')
+    expect(linter).toEqual({
+      pattern: '**/*.js',
+      commands: 'any-js',
+      fileList: [
+        `test.js`,
+        `deeper/test.js`,
+        `deeper/test2.js`,
+        `even/deeper/test.js`,
+        `.hidden/test.js`
+      ].map(path.normalize)
+    })
+  })
+
   it('should match pattern "deeper/*.js"', () => {
     const result = generateTasks(config, files)
     const linter = result.find(item => item.pattern === 'deeper/*.js')
diff --git a/test/getConfig.spec.js b/test/getConfig.spec.js
index 3ffd0eb..2bf4d10 100644
--- a/test/getConfig.spec.js
+++ b/test/getConfig.spec.js
@@ -141,7 +141,8 @@ describe('getConfig', () => {
       },
       ignore: ['docs/**/*.js'],
       subTaskConcurrency: 10,
-      renderer: 'custom'
+      renderer: 'custom',
+      relative: true
     }
     expect(getConfig(cloneDeep(src))).toEqual(src)
   })